재귀 타입
재귀 타입
function fibonacci(num) {
if (num <= 1) return 1
return fibonacci(num - 1) + fibonacci(num - 2);
}
함수가 자기 자신을 다시 호출하는 재귀 함수처럼 타입스크립트에도 재귀 타입이 있다.
type Recursive = {
name: string;
children: Recursive[];
}
const recur1: Recursive = {
name: 'test',
children: [],
}
const recur2: Recursive = {
name: 'test',
children: [
{ name: 'test2', children: [] },
{ name: 'test3', children: [] },
]
}
자기 자신을 타입으로 다시 사용하는 타입을 재귀 타입이라 한다.
컨디셔널 타입에서도 사용 가능
// 재귀적으로 배열의 요소 내부 접근해서 요소 타입을 겟
type ElementType<T> = T extends any[] ? ElementType<T[number]> : T;
타입 인수로 사용하는 것은 불가능
이 경우는 타입 인수를 쓰지 않는 방식으로 수정해야함
type T = number | string | Record<string, T>; // error
type T = number | string | { [key: string]: T };
재귀 타입도 맥시멈 콜스택과 비슷한 에러 발생 가능
재귀 타입을 선언할 때가 아닌 사용할 때 에러가 발생
type InfiniteRecur<T> = { item: InfiniteRecur<T> };
type Unwrap<T> = T extends { item: infer U } ? Unwrap<U> : T;
type Result = Unwrap<InfiniteRecur<any>>; // error
// Unwrap<{ item: { item: { item: 'hi' } } } > -> hi
// InfiniteRecur 타입은 무한하여 Unwrap 타입은 유한한 시간 안에 InfiniteRecur 타입을 처리할 수 없음##복잡한 구조도 쉽게 표현 가능
복잡한 구조도 쉽게 표현 가능
재귀 타입을 사용하는 대표적인 예시는 JSON
이와 같이 재귀 타입을 사용하면 복잡한 구조도 쉽게 표현 가능
type JSONType =
| string
| boolean
| number
| null
| JSONType[]
| { [key: string]: JSONType };
const a: JSONType = 'string';
const b: JSONType = [1, false, { "hi": "json" }];
const c: JSONType = {
prop: null,
arr: [{}],
}
배열 타입을 거꾸로 뒤집는 것도 가능
type Reverse<T> = T extends [...infer L, infer R] ? [R, ...Reverse<L>] : []
매개변수 순서를 바꾸는 타입 (매개변수는 반공변성)
// 내가 첫 시도한 타입인데...
type ReverseParams<T> = T extends (...infer LP, infer RP) => any
? [RP, ...ReverseParms<LP>] : []
// 정답
type FlipArguments<T> = T extends (...args: infer A) => infer R
? (...args: Reverse<A>) => R
: never
// 반환값과 매개변수의 타입을 추론한 뒤, 매개변수의 Reverse 타입 적용,
// A는 이미 매개변수의 튜플 상태이므로 바로 Reverse 타입 적용 가능
type Flipped = FlipArguments<(a: string, b: number, c: boolean) => string>;
Last updated