iamkanguk.dev

[타입챌린지-EASY] 189: Awaited 본문

Type Challenge

[타입챌린지-EASY] 189: Awaited

iamkanguk 2024. 3. 18. 14:46

문제

https://github.com/iamkanguk97/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;
    
    
/* _____________ 테스트 케이스 _____________ */
import type { Equal, Expect } from '@type-challenges/utils'

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>

 

풀이 해설

(1) MyAwaited<T extends PromiseLike<any>>

우리는 문제에서 알 수 있듯이 T는 Promise 또는 then을 메서드로 가지는 Promise와 같은 타입을 받아야 한다. 그래서 extends를 통해 제네릭의 타입을 제한해준다. 그래서 extends PromiseLike<any>라고 코드를 작성했는데 왜 Promise<any>는 되지 않는지에 대해서 아래에서 설명하도록 하겠다.

(2) T extends PromiseLike<infer R> ? {} : never

중간에 조건연산자 있는 부분을 빼고 큰 부분만 봐보자. T는 Promise 형태를 가지는데 그렇지 않으면 never를 반환한다. Promise 형태로 되어있다면 R에 해당 타입이 할당된다. 테스트 케이스와 함께 보자.

  • Promise<string> => R: string
  • Promise<{ field: number }> => R: { field: number }
  • Promise<Promise<string | number>> => R: Promise<string | number>

(3)  R extends PromiseLike<any> ? MyAwaited<R> : R

2번에서 반환받은 R이 Promise 형태를 가지고 있으면 MyAwaited를 재귀적으로 호출하는 것이고, 그렇지 않다면 그대로 R을 반환한다.

그러면 지금 위 2번의 3개 케이스를 보면 1번과 2번은 그대로 반환될 것이다. 하지만 3번 같은 경우에는 지금 Promise 형태를 띄고 있기 때문에 MyAwaited를 재귀적으로 호출하게 된다.

 

3번 케이스를 예시로 들면 string | number가 다시 R에 할당이 될 것이고, 3번에서 R이 Promise 형태를 띄지 않기 때문에 그대로 반환되어 string | number가 나오게 된다.

 

몰랐던 사실

사실 위의 설명으로 이해가 가능할 것이라고 생각한다. 하지만 필자가 제일 이해가 되지 않았던 부분은 PromiseLike를 사용해야 문제가 정답처리가 된다는 점이었다. Promise와 PromiseLike의 가장 큰 차이점을 보면 Promise는 현재 then ,catch ,finally가 구현이 되어있다. 하지만 PromiseLike는 오로지 then만 정의되어 있다.

 

마지막 테스트 케이스를 보면 Promise의 then 메서드가 작성되어 있지만 Promise 형태는 아니었다. 즉, Promise 동작과 비슷하게 then 메서드를 가진 타입이라고 할 수 있다. 그래서 우리는 이런 경우를 처리하기 위해 PromiseLike를 사용했다고 생각하면 된다.

 

후기

필자는 좀 많이 어려웠던 문제였던 것 같다. 아예 감이 잡히지 않아서 좀 고민하다가 그냥 다른 분들의 풀이를 보면서 분석해가며 이해를 하게 되었다. 


참고자료

- https://jaenny-dev.tistory.com/6

 

[타입챌린지/type-challenge] Awaited

문제 Promise와 같은 타입에 감싸진 타입이 있을 때, Promise 내부에 감싸진 타입이 무엇인지 어떻게 알 수 있을까요? 예를 들어, Promise이 있을 때 ExampleType을 어떻게 얻을 수 있을까요? type ExampleType =

jaenny-dev.tistory.com

 

- https://suloth.tistory.com/31

 

타입챌린지 : 189-Awaited (easy)

이 글은 제가 타입챌린지를 하면서 해석한 내용을 적는 글입니다. 틀린 내용이 있으면 댓글 달아주시면 감사하겠습니다. https://github.com/type-challenges/type-challenges/blob/main/questions/00189-easy-awaited/READM

suloth.tistory.com