2. polymorphism
poly란?
- many, serveral, much, multi 등과 같은 뜻이다.
morphos란?
- form, strucure등과 같은 뜻이다.
따라서 polymorphism은 다형성을 뜻한다
기본적으로 함수는 여러가지 다른 모양을 가지고 있다.
타입스크립트에서 함수는 다른 2~3개의 parameter를 가질 수 있다.
또는 타입스크립트에서 함수는 string이나 object를 첫번째 파라미터로 가질 수 있다고 했다.
그러니까 우리는 이미 약간의 여러가지 모양의 다형성을 해본것이다.
이번시간에는 여기에 더 도움을 줄 제네릭에 대해서 알아볼 것이다.
제네릭은 선언 시점이 아니라 생성 시점에 타입을 명시하여 하나의 타입만이 아닌 다양한 타입을 사용할 수 있도록 하는 기법이다.
type SuperPrint = {
(arr: number[]): void;
(arr: boolean[]): void;
(arr: string[]): 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(["a", true, 1]) 이런식으로 섞는다면 오류가 뜬다.
이럴때는 type에다가 (arr: (string | boolean | number)[]):void를 추가해줘야한다.
하지만 이렇게 하면 번거롭다. 더 쉽게하는 방법이 존재한다.
바로 "generic"을 사용하는 것이다.
generic은 위와 같은 상황이나 타입이 어떤것인지 미리 알 수 없을 때 사용한다.
사용방법은 아래와 같다.
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, "a"]);
이렇게 <T>를 넣어주면 된다. name은 원하는 것 아무거나 가능하다 <potato>이렇게 해도 된다.
대부분 <T, V> 을 많이 사용한다.
만약 return하는 함수이면 아래처럼 수정해주면 된다.
type SuperPrint = {
<T>(arr: T[]): T;
};
const superPrint: SuperPrint = (arr) => arr[0];
superPrint([1, 2, 3, 4]);
superPrint([true, false, true]);
superPrint(["a", "b", "c"]);
superPrint([1, 2, true, false, "a"]);
여기서 any와 비슷하다고 느낄 수도 있는데
any와 generics가 어떤 타입이든 받을 수 있다는 점에서 서로 같지만
any는 any로서 밖에 알 수 없지만 generics는 타입 정보를 알 수 있다. 그래서 다른 쪽으로 전달 할 수도 있다.
3. Generics recap
예를들어 매개변수를 하나 더 늘린다고 할때 제네릭을 또 사용할 수 있다.
type SuperPrint = <T,M>(a: T[], b:M): T;
const superPrint: SuperPrint = (arr) => arr[0];
superPrint([1, 2, 3, 4], "x");
usperPrint(["a","b","c"], false)
이런식으로 두 개의 매개변수를 설정해 줄 수 있다.
4. 실제 사용 사례
type PPlayer<E> = {
name: string;
extraInfo: E;
// extrinfo는 어떤 type이든 될 수 있으니 제네릭을 사용하자
//any를 사용할 수 있지만 any를 사용하면 보호를 못 받으니까 any대신 제네릭 사용
//꼭 E라고 할 필요없음 다르게 가능, 단지 대문자로만 시작하면 된다.
};
const koko: PPlayer<{ favFood: string }> = {
name: "koko",
extraInfo: {
favFood: "kimchi",
},
};
아래와 같이 해도 똑같다.
type kokoPlayer = PPlayer<{ favFood: string }>;
const koko: kokoPlayer = {
name: "koko",
extraInfo: {
favFood: "kimchi",
},
};
이렇게 세분화 해도 위에 두 코드와 같다
type KokoExtra = {
favFood: string;
};
type kokoPlayer = PPlayer<KokoExtra>;
const koko: kokoPlayer = {
name: "koko",
extraInfo: {
favFood: "kimchi",
},
};
여기에 추가적으로 또 다른 player도 만들 수 있다.
const lynn: PPlayer<null> = {
name: "lynn",
extraInfo: null,
};
함수 말고도 다른 곳에서도 사용이 된다.
type A = Array<number>
let a:A = [1,2,3,4]
함수에서 사용할 때 이렇게 두 가지가 가능하다
function printAllNumbers(arr: number[])
또는
function printAllNumbers(arr: Array<number>)
react.js에서 useState()도 제네릭을 받는다.
그냥 useState()만 쓴다면 타입스크립트는 우리의 state타입을 알 수 없다.
그래서 useState<number>() 처럼 해줘야한다.
이렇게하면 state의 타입은 number이 된다.
'Typescript > 개념' 카테고리의 다른 글
[TypeScript] generics (0) | 2023.02.15 |
---|---|
[Typescript] void (0) | 2023.02.14 |
[Typescript] declare (0) | 2022.12.28 |
[Typescript] 4. Class and Interfaces (1) | 2022.09.27 |
[Typescript] call signatures // overloading (0) | 2022.09.22 |