Type Challenges

Awaited

#TypeScript

189 - Awaited

Promise와 같은 타입에 감싸인 타입이 있을 때, 안에 감싸인 타입이 무엇인지 어떻게 알 수 있을까요?

type ExampleType = Promise<string>;
 
type Result = MyAwaited<ExampleType>; // string

풀이

type Thenable<T> = {
  then: (_: (_: T) => any) => any;
};
 
type MyAwaited<T extends Thenable<any>> = T extends Thenable<infer U>
  ? U extends Thenable<any>
    ? MyAwaited<U>
    : U
  : never;
type MyAwaited<T extends object> = T extends {
  then: (onfulfilled: (value: infer U) => any) => any;
}
  ? U extends { then: (onfulfilled: (value: any) => any) => any }
    ? MyAwaited<U>
    : U
  : never;

원래는 아래와 같이 풀었는데 이해를 돕기 위해 이슈를 보고 위 코드의 Thenable을 추가했다.
위 코드에서 then은 객체 내부{ ... }의 key로 정확히 then이라는 이름을 가져야하지만,
onfulfilledvalue는 함수 내부( .... )의 파라미터의 타입을 지정하기 위해 넣은 이름일 뿐,
함수의 파라미터가 onfulfilledvalue와 같은 이름을 가져야하는 것이 아니다.
Thenable을 보면 특정 이름이 아니라 _를 사용하는 것을 볼 수 있다.
또, 주목하면 좋을 부분은 type 내에서 재귀가 가능하다는 것이다.
이쯤되면 단순히 타입을 지정하는 게 아니라 그냥 거의 함수 수준인데
extends를 이용한 조건문 삼항 연산자를 통해서 자기 자신을 다시 불러서 재귀적으로 type을 정의할 수 있다.

Reference

Type Challenges, 189 - Awaited