타입 챌린지 189 - Awaited

소파의 벨로그·2025년 2월 25일

타입챌린지

목록 보기
6/131

문제 링크

문제

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

예시:Promise<ExampleType>이 있을 때, ExampleType을 어떻게 얻을 수 있을까요?

내 풀이

type MyAwaited<T extends PromiseLike<any>> =
  T extends PromiseLike<infer K>?
    (K extends PromiseLike<any>?
      (MyAwaited<K>)
      :(K)
	)
  :(never);

나는 conditional type이 여러 개 올 경우 디버깅을 위해 괄호를 사용하는 편이다.
또, 중첩된 경우 참과 거짓에 들어갈 곳에 다시 괄호를 사용한다

조건?
	(참)
    :(거짓)

infer 키워드를 통해 PromiseLike의 제네릭을 추론 후,

그 제네릭이 PromiseLike에 호환 가능하면 재귀적으로 타입을 확인하고,
그렇지 않으면 추론된 타입이 결과 타입이 된다.

{ then: (onfulfilled: (arg: number) => any) => any }과 같은 반례 해결을 위해
받을 수 있는 타입을 Promise가 아닌 PromiseLike로 값을 받았다

관련 개념

PromiseLike타입은
index 접근이 가능하고,length 프로퍼티를 갖는 ArrayLike 개념과 유사하다.
Promise 타입과 유사한 then이 구현되어 있는 타입이면 PromiseLike로 보는 듯 하다

// lib.es5.d.ts
interface PromiseLike<T> {
    then<TResult1 = T, TResult2 = never>(
    onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>)| undefined | null,
    onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null
    ): PromiseLike<TResult1 | TResult2>;
}

다른 블로그를 참고해서 이 타입의 기원을 알 수 있었다.
Promise가 공식 기능으로 채택되기 전, 다양한 라이브러리들이 존재했는데, 그 라이브러리들과의 호환을 위해 이런 타입이 생겨난 것 같다.
(그래서 ArrayLike는 공식 문서가 있지만, PromiseLike라는 개념은 없는 듯 하다)

다른사람의 풀이

PromiseLike에 대응하기 위해 custom type을 만든 사람도 있었고,

문제에 PromiseLike를 대응하지 않은 답안도 존재했다.(아마 이전에는 PromiseLike 대응이 필요 없었을 듯)

참고자료

https://yceffort.kr/2021/11/array-arraylike-promise-promiselike

0개의 댓글