타입스크립트를 공부하던 중에 Generic이라는 개념에 대해서 배웠습니다. 언제 사용하는 건지 간단한 예시를 통해 정리를 해놓으려고 합니다.



✅ Generic을 쓰는 이유가 뭘까?

하나의 함수를 만든다고 가정해봅시다. 파라미터로 하나의 Array를 받을 건데, 이 배열의 각 요소들이 어떤 타입을 가지고 있을지는 모릅니다. 이럴 때 어떻게 해야할까요?

한 가지 방법은 경우의 수를 모두 적어놓는 것입니다.
숫자로 이루어진 배열, 문자로 이루어진 배열, boolean으로 이루어진 배열이 들어올 것이라고 가정하고 타입을 생성합니다.

1
2
3
4
5
6
7
8
9
10
11
12
type somethingPrint = {
(arr: number[]): void
(arr: string[]): void
(arr: boolean[]): void
}

const somethingPrint = (arr) => {
arr.forEach(i => console.log(i)
}

somethingPrint([1, 2, 3])
somethingPrint(["1", "2", "3"])

이렇게 사용하면 문제는 없습니다만, 만약 somethingPrint([1, 2, "a", "b"])와 같이 문자와 숫자로 이뤄진 배열을 받게되는 순간 에러가 발생합니다. 쓰다보니 좋은 방법이 아니라는 생각이 듭니다.. 🤔

👉🏻 이럴 때 사용하는 것이 Generic!


✅ Generic 사용방법

<>를 사용해서 Generic을 생성합니다. <> 안에 들어가는 명칭은 무엇이든 상관은 없으나, 외부 라이브러리들 타입 선언해놓은 것들을 보면 T, V를 많이 사용하는 것 같습니다.

1
2
3
4
5
6
7
8
9
10
11
type somethingPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): void;
};

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

somethingPrint([1, 2, 3]);
somethingPrint(['1,', '2', '3']);
somethingPrint([1, 2, 'a', 'b']);

타입스크립트는 타입을 추론해서 생성하는 것을 확인할 수 있습니다.


위 코드는 타입을 생성할 때 void로 리턴하는게 없다는 것을 명시했습니다..근데 만약에 무언가를 리턴하는 함수라면 어떻게 해야할까요?

1
2
3
type somethingPrint = {
<TypePlaceholder>(arr: TypePlaceholder[]): TypePlaceholder; // ✅ 리턴값의 타입을 명시
};
1
2
3
type somethingPrint = {
<T>(arr: T[]): T;
};

✅ 그래서 이거 언제 사용하는 걸까?

하나의 Player 객체를 만들려고하는데 name이라는 프로퍼티는 필수적으로 만들예정이지만 extraInfo에는 어떤 프로퍼티가 들어올지 아직 모른다고 했을 때 Generic을 사용해서 타입을 정의해주면 됩니다. :)

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

≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈≈

const leejaelll:Player<{favFood: string}> ={
name: 'leejaerin',
extraInfo: {
favFood: "pizza"
}
}

// 🚧 동일하지만 다르게 쓸 수도 있음
type leejaelllPlayer = Player<{favFood: string}>

const leejaelll:leejaelllPlayer ={
name: 'leejaerin',
extraInfo: {
favFood: "pizza"
}
}