함수는 기본적으로 다른 모습(Polymorphism)을 가지고 있다.
우선 이러한 함수가 있다고 가정해보자.
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"])
이럴 경우, call signature가 3개 필요하다.
하지만 모든 Call signature를 만들기 귀찮고, 어떤 타입이든 만들고 싶다면, 이렇게 할 수 있다.
이때, 타입스크리트에게 타입을 추론하도록 한다.
우선, 제너릭이라는 개념을 알아보자.
type SuperPrint = {
<TypePlaceHolder>(arr: TypePlaceHolder[]): void
}
여기서, TypePlaceHolder가 바로 Generic이다.
이렇게 진행한다면, 함수에 값을 입력할 때, TS가 자동으로 타입을 추론해준다.
const superPrint: SuperPrint == (arr) => {
arr.forEach(i => console.log(i))
}
superPrint([1, 2, 3, 4]) // const superPrint: <number>(arr: number[]) => void
superPrint([true, false, true]) // const superPrint: <boolean>(arr: boolean[]) => void
superPrint(["a", "b", "c"]) // const superPrint: <string>(arr: string[]) => void
superPrint([true, false, 1]) // const superPrint: <boolean | number>(arr: (boolean | number)[]) => void
제너릭은 함수의 타입을 추론하여 코드의 길이를 줄여준다.
또한, 들어올 타입을 생각하지 못하더라도 작성이 가능하도록 한다.
물론, Any를 사용하더라도 어떠한 문제가 발생하지 않는다.
하지만, 제너릭을 사용하여야 TS의 타입 보호를 받을 수 있으므로 Generic을 사용하도록 하자.
player라는 타입을 만들 때, 어떤 값이 이름과 함께 들어온다고 생각해보자.
type Player<E> = {
name: string
extraInfo: E
}
이렇게 사용할 경우, 타입을 선언하는 변수별로 다른 값을 넣는 것이 가능하다.
type AddNew = {
favFood: string
}
type player = Player<AddNew> // 새로운 타입
const onve: player = {
name: "onve",
AddNew: {
favFood: "Hambuger"
}
}
type player1: Player<null>
const yk: player1 = {
name: "yk"
}
이러한 Type에서의 사용은 하나의 Type을 매우 많은 곳에 사용할 수 있도록 해준다.
*본 내용은 노마드코더 "Typescript로 블록체인 만들기"에서 습득한 내용을 바탕으로 재구성한 것 입니다.