포스트

타입스크립트의 제너릭

제너릭을 안 쓸때의 문제점

타입스크립트의 제너릭은 무엇일까? 일단 제너릭을 알기전에 제너릭을 안쓸때의 문제점을 보자

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
type SuperPrint = {
  // 함수의 타입을 일일이 정의를 해줘야된다.
  (arr: number[]): void;
  (arr: boolean[]): void;
  (arr: string[]): void;
  (arr: (number | boolean)[]): void;
};

const superPrint: SuperPrint = (arr) => {
  arr.forEach((i) => console.log(i));
};

superPrint([1, 2, 3, 4]);
superPrint([true, false, true]);
superPrint(["a", "b", "c"]);
superPrint([1, 2, true, false]);

배열의 내용을 하나씩 다 출력하는 함수를 만든다고 했을때 type에 알지 못하는 타입의 배열의 타입이 들어갈 경우 타입에 일일이 하나씩 정의를 해야되는 문제가 발생한다.

제너릭으로의 해결

코드를 제너릭을 사용하면 다음과 같이 간단하게 정리가 된다.

1
2
3
4
5
6
7
8
9
10
11
12
13
type SuperPrint = {
  // 제너릭을 통해 타입을 간단히 정리하였다.
  <T>(arr: T[]): void;
};

const superPrint: SuperPrint = (arr) => {
  arr.forEach((i) => console.log(i));
};

superPrint([1, 2, 3, 4]);
superPrint([true, false, true]);
superPrint(["a", "b", "c"]);
superPrint([1, 2, true, false]);

<T> 에서 call signature를 맞춰줌으로써 타입에 대한 값이 생긴다.

다수의 제너릭을 사용하는 방법

제너릭은 인자 들어가는 부분부터 타입이 인지가 되기에 두개를 쓸때는 아래와 같이 사용한다.

1
2
3
4
5
6
7
8
9
10
11
12
type SuperPrint = {
  <T, M>(arr: T[], value: M): void;
};

const superPrint: SuperPrint = (arr, val) => {
  arr.forEach((i) => console.log(i));
};

superPrint([1, 2, 3, 4], "x");
superPrint([true, false, true], 1);
superPrint(["a", "b", "c"], true);
superPrint([1, 2, true, false], []);

function 으로 Generics 정의하기

1
2
3
4
5
6
7
8
9
10
11
12
function superPrint1<T>(a: T[]) {
  return a[0];
}

superPrint1([1, 2, 3, 4]);

// superPrint1<boolean>([1, 2, 3, 4]);  // error!
// 제너릭으로 타입을 직접 정의할 수 있지만 TypeScript가 자연스럽게 처리 되도록 하는게 좋다.

superPrint1([true, false, true]);
superPrint1(["a", "b", "c"]);
superPrint1([1, 2, true, false]);

함수에서 superPrint1<boolean>([1, 2, 3, 4]) 와 같이 제너릭을 직접적으로 선언할 수 도 있지만, 자연스럽게 타입스크립트에서 처리되도록 할 수 있는게 좋다.

type에서 Generics 사용하기

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
type Player<E> = {
  name: string;
  extraInfo: E;
};

type Extra = {
  HP: number;
};

type ParkPlayer = Player<Extra>;

const Park: ParkPlayer = {
  name: "park",
  extraInfo: {
    HP: 1000
  }
};

const Lee: Player<null> = {
  name: "Lee",
  extraInfo: null
};

타입 안에 타입형식으로 다음과 같이도 사용할 수 있다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.