Promise와 같은 타입에 감싸인 타입이 있을 때, 안에 감싸인 타입이 무엇인지 어떻게 알 수 있을까요?
type ExampleType = Promise<string>
type Result = MyAwaited<ExampleType> // string
정답
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer U> ? U extends PromiseLike<any> ? MyAwaited<U> : U : never;
PromiseLike
Promise뿐만 아니라 then을 가진 커스텀 객체나 구형 라이브러리의 Promise까지 모두 포함.
MyAwaited<U>
만약 T가 Promise<Promise<string|number>> 일 경우를 대비하여 만약 U가 PromiseLike라면 재귀로 한 번 더 검증을 한다.
조건 C, 참일 때 반환하는 타입 T, 거짓일 때 반환하는 타입 F를 받는 타입 If를 구현하세요. C는 true 또는 false이고, T와 F는 아무 타입입니다.
type A = If<true, 'a', 'b'> // expected to be 'a'
type B = If<false, 'a', 'b'> // expected to be 'b'
정답
type If<C extends boolean, T, F> = C extends true ? T : F;
JavaScript의 Array.concat 함수를 타입 시스템에서 구현하세요. 타입은 두 인수를 받고, 인수를 왼쪽부터 concat한 새로운 배열을 반환해야 합니다.
type Result = Concat<[1], [2]> // expected to be [1, 2]
정답
type Concat<T extends readonly any[], U extends readonly any[]> = [...T, ...U];
타입스크립트에서도 타입에 스프레드 연산자 가능! 정확하겐 Variadic Tuple Types (가변 튜플 타입) 을 사용한다고 함.
Array.push의 제네릭 버전을 구현하세요.
type Result = Push<[1, 2], '3'> // [1, 2, '3']
정답
type Push<T extends readonly any[], U> = [...T, U];
다른사람 풀이
type Push<T extends readonly unknown[], U> = [...T, U];
나는 any를 썼고 다른 사람은 unknown을 썼다.
이 둘의 큰 차이는 뭘까?
타입스크립트 컴파일러에서
any: 타입 검사를 무시함. 어떤 타입이 들어와도 상관 안함. ⇒ 그래서 런타임 에러가 발생할 수 있음.
unknown: any와 같이 모든 타입을 받을 수 있다. 하지만 쓰려면 반드시 타입 확인이 필요하다.
타입 오염
제네릭이나 교차 타입(&)에서 any는 다른 타입을 덮어쓴다.
type T1 = any & string; // 결과: any (string이 잡아먹힘)
type T2 = unknown & string; // 결과: string (string이 살아남음)
any보다 unknown이 안전하기 때문에 unknown을 쓰는걸 추천한다고 함.