ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [TypeScript] 타입스크립트 - Generic
    Frontend 2020. 3. 3. 21:49

     

     

    function generateString(message: string): string {
      return message;
    }
    function generateNumber(message: number): number {
        return message;
    }

    위 예시와 같이 string을 인자로 받아서 string을 리턴하는 함수와

    number를 인자로 받아서 number를 리턴하는 함수가 있다고 해보자.

     

    두 개 모두 인자를 받아서 그대로 리턴하는 아주 단순한 함수이다.

    그런데 하나는 string을 처리하고 하나는 number를 처리한다는 차이점 밖에 없다.

     

    이렇게 동일한 로직을 수행하는 함수를 단지 타입이 다르다는 이유로

    별개의 함수로 구현한다는 것은 매우 비효율적인 일이다.

    바로 이러한 상황에 유용한 것이 바로 Generic이다.

     

     

    function generate<T>(message: T): T {
      return message;
    }
    
    generate<string>('Mark');
    generate<number>(3);

    Generic은 위와 같이 사용한다.

    함수 이름 옆에 <>를 쓰고 <> 안에 Generic으로 사용할 문자를 적어준다.

     

    예시에서는 T라고 적었다. 이제 이 함수는 T를 인자로 받아서 T를 리턴하게끔 되어있는데,

    이제 함수를 사용하는 시점에 Generic type을 지정해줄 수 있다!

    • 만약 함수 호출 시 <>로 Generic type을 쓰지 않으면 인자로 넣은 값으로 Ts compiler가 T를 추론한다.

    • Generic type을 쓰면 T를 내가 지정한 타입을 기준으로 판단한다.

     

    const a: string[] = ['a'];
    const b: Array = ['b']; // 내부적으로 generic을 이용한거임

    TypeScript에서 Array type을 지정할때 2가지 형태가 있는데,

    아래와 같은 표기가 바로 TypeScript에서 내부적으로 Generic을 이용한 표기이다.

     

     

    function hello<T>(messages: T[]): T {
      return messages[0];
    }

    배열의 item의 type을 Generic과 사용하고 싶다면 위와 같이 적용하면 된다.

     

     

    type PracticeGeneric = <T>(message: T) => T;
    
    const practice: PracticeGeneric = <T>(message: T): T => {
      return message;
    };
    
    practice<string>('hello world').length; // 제대로 작동 안하는 경우가 많아서 잘 사용하지 않음
    
    class Something<T> {
      private color: T;
    
      constructor(color: T) {
            this.color = color;
      }
    }
    
    const obj1 = new Something<string>('red');
    const obj2 = new Something<string>(3); // error

    클래스에서 Generic은 위와 같이 사용한다.

     

     

    Generic with extends

    class Person<T extends string | number> {
      private name: T;
    
      constructor(name: T) {
        this.name = name;
      }
    }
    
    new Person(false); // error

    Generic은 extends와 같이 사용할 수 있다.

     

     

    Generic with multiple types

    class Person<T, P> {
      private name: T;
      private age: P;
    
      constructor(name: T, age: P) {
        this.name = name;
        this.age = age;
      }
    }
    
    new Person<string, number>('Elise', 10);

    여러 개의 Generic도 사용 가능하다.

     

     

    Type lookup system

    interface Person {
      name: string;
      age: number;
    }
    
    const person: Person = {
      name = 'Jane';
      age: 20;
    };
    
    function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
      return obj[key];
    }
    
    function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
      obj[key] = value;
    }
    
    getProperty(person, 'name');
    setProperty(person, 'job', 'developer');

    위와 같이 Generic을 이용하여 많은 것들을 할 수 있다.

     

     

    Keyof

    interface Person {
      name: string;
      age: number;
    }
    
    type Prop = keyof Person; // 'name' | 'age'

    위에서 살짝 언급된 keyof는 어떤 interface의 key만 type으로 지정하는 것이다.

    즉, Prop은 Person interface의 name이나 age로만 사용 가능하다.

    반응형

    COMMENT