이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다.
https://github.com/type-challenges/type-challenges/blob/main/questions/00189-easy-awaited/README.md
type MyAwaited<T extends PromiseLike<any>> =
T extends PromiseLike<infer R> ?
R extends PromiseLike<any> ? MyAwaited<R> : R : never;
// test cases
type X = Promise<string>
type Y = Promise<{ field: number }>
type Z = Promise<Promise<string | number>>
type Z1 = Promise<Promise<Promise<string | boolean>>>
type T = { then: (onfulfilled: (arg: number) => any) => any }
type cases = [
Expect<Equal<MyAwaited<X>, string>>,
Expect<Equal<MyAwaited<Y>, { field: number }>>,
Expect<Equal<MyAwaited<Z>, string | number>>,
Expect<Equal<MyAwaited<Z1>, string | boolean>>,
Expect<Equal<MyAwaited<T>, number>>,
]
// @ts-expect-error
type error = MyAwaited<number>
PromiseLike는 Promise같은 객체들을 다루는 가리키는 타입이라고 생각하면 된다.
infer R은 변수를 지정하듯이 R이라는 타입 인자를 선언(초기화) 한다고 생각하자. 실제로... 그런 개념이랑은 좀 다른것 같지만.
처음에는
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer R> ? MyAwaited<R> : never; 를 생각했었다.
그런데 MyAwaited<R> 에서 R이 빨간줄이 뜨는 것이다... 그래서 R을 PromiseLike 타입을 받을 수 있도록 하려고 이런 저런 시도를 해봤는데, 결국 삼항연산자를 하나 더 쓰는게 답이었다. 처음에는 infer R extends PromiseLike<any> 같은 시도도 해봤으나 문법 오류였다 ㅋㅋ
type MyAwaited : MyAwaited 타입을 정의한다
<T extneds PromiseLike<any>> : T는 any 타입을 리턴하는 PromiseLike 형태이다 라는 뜻
조건 1. T extends PromiseLike<infer R> ? : 만약 T가 R이라는 타입을 리턴 타입으로 가지는 PromiseLike 이라면
조건 2. R extends PromiseLike<any> ? : 만약 R이 PromiseLike<any> 타입이라면 (재귀 형태를 띄기 때문에 R을 PromiseLike<any>로 추론하게 하려면 이런 조건이 들어가야한다)
MyAwaited<R> : R : never : 조건 1이 참이라면 MyAwaited<R>을 리턴, 조건 2가 참이라면 R을 리턴, 그 이외는 never을 리턴한다.
복잡하게 보이지만 생각보다 간단하다.
위의 해석이 복잡하다고 생각되는 사람은 이 구문을 먼저 봐보자.
type MyAwaited<T> = T extends PromiseLike<infer R> ? MyAwaited<R> : never;
위 구문을 이해했다면, error를 발생시켜야하는 case인 MyAwaited<number>가 에러를 발생시키지 않는 다는 것을 깨닫게 될 것이다.
그렇다면, 제네릭 인자로 number를 받았을 때 에러를 뱉게 해야하는데, 이 방법이 바로 T에 PromiseLike를 상속하는 것이다.
따라서,
type MyAwaited<T extends PromiseLike<any>> = T extends PromiseLike<infer R> ? MyAwaited<R> : never;
하지만 여기서 MyAwaited<R> 에서 에러가 난다. MyAwaited는 PromiseLike를 제네릭으로 받는데, R이 PromiseLike 타입이 아니기 때문이다.
그래서 R이 PromiseLike 이다 라는 조건을 추가해주면
라는 답이 나온다.
'Language > Typescript' 카테고리의 다른 글
타입챌린지 : 533-Concat (easy) (0) | 2023.03.04 |
---|---|
타입챌린지 : 268-If (easy) (0) | 2023.03.04 |
타입챌린지 : 43-Exclude (easy) (0) | 2023.03.02 |
타입챌린지 : 18-Length of Tuple (easy) (0) | 2023.03.02 |
타입챌린지 : 14-First of Array (easy) (0) | 2023.03.02 |