혼자 적어보는 노트

[TypeScript] 제네릭(Generics) 본문

Typescript

[TypeScript] 제네릭(Generics)

jinist 2022. 5. 23. 04:56

 

제네릭에 대해 알아보자.

 

제네릭(Generics)

제네릭은 재사용성이 높은 컴포넌트를 만들기 위해 활용된다.

 

아래와 같이 함수에 세부적인 타입을 미리 지정해버리면

해당 함수는 무조건 number값만 받아야 하고 재사용성이 떨어지게 된다.

function returnText(text: number): number{
  return text;
}

 

다른 타입의 값도 받고 싶다면 any를 사용해야 하는데

any를 사용할 경우 함수의 인자로 어떤 타입이 전달되었는지 알 수 없고

어떤 값이 반환되는지 알 수 없다.

 

이 때 ✨제네릭을 사용해서 재사용성을 높일 수 있다.

 

제네릭 기본 문법

function returnText<T>(text: T): T{
  return text;
}

 returnText('Hello');
 returnText(123);

위 처럼 제네릭 문법을 사용하면 사용자가 값을 전달할 때 어떤 타입인지 정할 수 있고

리턴받는 타입을 보장 받을 수 있다.

 

조금 더 명확하게 전달하려면 아래와와 같이 전달할 타입을 지정할 수 있다.

 returnText<string>('Hello');
 returnText<number>(123);

 

제네릭에 타입 지정

  function returnText<T>(text: T): T{
    console.log(text.length); // Error
    return text;
  }

전달할 인자가 배열이라는 가정하에 위 코드를 실행시키면 에러가 발생한다.

인자로 받은 text에 어떤 값이 들어오는지 컴파일 시점에선 알 수가 없기 때문에

length를 확인하려고 하면 에러가 발생하는 것이다.

 

아래와 같이 배열 타입을 지정해주면 에러없이 실행이 가능하다.

  function returnText<T>(text: T[]): T[]{
    console.log(text.length)
    return text;
  }
  
   returnText(["Hello", 2, 3]);

 

위 부분은 아래와 같이 명시적으로 선언할 수도 있다.

returnText<T>(text: Array<T>): Array<T>

 

 

제네릭의 제약 조건

위의 코드처럼 배열 타입만을 전달 할 수 있게 지정해버리면

다른 데이터 타입에 length속성이 있더라도 전달이 되지 않는다는 문제점이 있다.

 

interface와 extends를 사용하여 length에 대해서 동작을 하는 인자만 넘겨 받을 수 있게 할 수 있다.

interface lengthWise{
    length: number
  }

  function returnText<T extends lengthWise>(text: T): T{
    console.log(text.length)
    return text;
  }
  
   returnText(["Hello", 2, 3]);
   returnText("Hello");

 

클래스에서의 제네릭

기본적으로 클래스에서의 제네릭은 아래와 같이 사용할 수 있다.

  class Generic<T>{
    constructor(private name:T){};

    returnGeneric():T{
      return this.name;
    }
  }

  const generic = new Generic("Jay");
  console.log(generic.returnGeneric()); // Jay

 

인터페이스와 함께 구현 시 아래와 같이 사용할 수 있다.

  interface GenericReturnText<F,S> {
    first: () => F;
    second: () => S;
  }

  class ReturnText<F, S> implements GenericReturnText<F, S> {
    constructor(private firstValue: F, private secondValue: S){};

    first(): F{
      return this.firstValue;
    }
    second() :S{
      return this.secondValue;
    }
  }

  const returnText:GenericReturnText<string, number> = new ReturnText("Hello", 2);
  console.log(returnText.first());
  console.log(returnText.second());

 

객체의 속성 제약

extends keyof를 사용하여 객체에 없는 속성에는 접근하지 못하게 제한할 수 있다.

  const obj = {
    name: 'Jay',
    age: '22'
  };

  function getValue<T, K extends keyof T>(user: T, key: K): T[K]{
    return user[key];
  }

  console.log(getValue(obj, 'name')) // Jay
  console.log(getValue(obj, 'gender')) // Error

 

 

한번에 학습하려니 조금 복잡한 면이 있어서 응용을 잘 할 수 있을지는 모르겠지만,

이것 저것 혼자 만들어봐야 조금 더 감을 익힐 수 있을 것 같다.

 

 

참고:

https://joshua1988.github.io/ts/guide/generics.html#%EC%A0%9C%EB%84%A4%EB%A6%AD-generics-%EC%9D%98-%EC%82%AC%EC%A0%84%EC%A0%81-%EC%A0%95%EC%9D%98

https://academy.dream-coding.com/courses/typescript

 

Comments