제네릭을 어디에 선언할 수 있을까?
TypeScript에선 호출 시그니처를 정의하는 방법에 따라 제네릭을 추가하는 방법이 정해져 있습니다.
type Filter = {<T>(array: T[], f: (item: T) => boolean) =>T[]} // ①
let filter: Filter = //...
type Filter<T> = { // ②
(array: T[], f: (item:T) => boolean): T[]
}
let filter: Filter<number> = // ...
type Filter = <T>(array: T[], f: (item: T) => boolean) => T[] // ③
let filter: Filter = // ...
type Filter<T> = (array: T[], f: (item: T) => boolean) => T[] // ④
let filter: Filter<string> = // ...
function filter<T>(array: T[], f: (item: T) => boolean): T[]{ // ⑤
// ...
}
① T의 범위를 개별 시그니처로 한정한 전체 호출 시그니처입니다. T를 한 시그니처 범위로 한정했으므로 TypeScript는 filter 타입의 함수를 호출할 때 이 시그니처의 T를 구체 타입으로 한정합니다. 각각의 filter 호출은 자신만의 T 한정 값을 갖습니다.
② T의 범위를 모든 시그니처로 한정한 전체 호출 시그니처입니다. T를 Filter 타입의 일부로 선언했으므로 TypeScript는 Filter 타입의 함수를 선언할 때 T를 한정합니다.
③ ①과 비슷하지만 전체 시그니처가 아니라 단축 호출 시그니처입니다.
④ ②와 비슷하지만 전체 시그니처가 아니라 단축 호출 시그니처입니다.
⑤ T를 시그니처 범위로 한정한, 이름을 갖는 함수 호출 시그니처입니다. filter를 호출할 때 T타입으로 한정하므로 각 filter 호출은 자신만의 T 한정 값을 갖습니다.
두 번째 예로 map 함수를 구현해 보겠습니다.
map은 filter와 비슷하지만 배열에서 항목을 제거하는 대신 매핑 함수를 이용하여 각 항목을 변환합니다.
구현부터 시작하겠습니다.
function map(array: unknown[], f: (item: unknown) => unknown): unknown[] {
let result = []
for (let i = 0; i < array.length; i++){
result[i] = f(array[i])
}
return result
}
위의 코드를 제네릭으로 변환하면 아래와 같습니다.
function map<T,U>(array: T[], f: (item: T) => U): U[] {
let result = []
for (let i = 0; i < array.length; i++){
result[i] = f(array[i])
}
return result
}
인수 배열 멤버의 타입을 대변하는 T, 반환 배열 멤버 타입을 대변하는 U, 이렇게 두 가지 제네릭 타입이 필요합니다.
T타입의 요소를 포함하는 배열을 전달하면 매핑 함수가 T 타입의 값을 가지고 U 타입의 값으로 변환합니다.
그리고 최종적으로 U 타입의 항목을 포함하는 배열을 반환합니다.
'👶 TypeScript' 카테고리의 다른 글
제네릭 타입 별칭 (0) | 2023.01.13 |
---|---|
제네릭 타입 추론 (0) | 2023.01.13 |
언제 제네릭 타입이 한정되는가? (0) | 2023.01.12 |
다형성 (0) | 2023.01.12 |
오버로드된 함수 타입 (0) | 2023.01.12 |