0. 문제
Promise와 같은 타입에 감싸진 타입이 있을 때, 안에 감싸진 타입이 무엇인지 찾아보자
1. 설명
/* _____________ Your Code Here _____________ */
type MyAwaited<T> = T extends Promise<infer P> ? P extends Promise<any> ? MyAwaited<P> : P :
T extends { then: (onfulfilled: (arg: infer K) => any) => any } ? K : error
type MyAwaited2<T extends {then:unknown}> = Awaited<Promise<T>>
/* _____________ Test Cases _____________ */
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>
추가설명
첫번째 방법
type MyAwaited<T> = T extends Promise<infer P> ? P extends Promise<any> ? MyAwaited<P> : P :
T extends { then: (onfulfilled: (arg: infer K) => any) => any } ? K : error
이런 방식으로 해결할 수 있다.
다중 if문을 쪼개서 생각해보자
T extends Promise<infer P> ? T가 Promise를 extends할 때 작동시킬 부분 : T가 Promise를 extends하지 않을 때 작동시킬 부분
- T가 Promise를 extends할 때 작동시킬 부분
P extends Promise<any> ? MyAwaited<P> : P
- P가 Promise를 extends한다면
MyAwaited<P>
로 재귀를 돌아주고, 그게 아니라면 그냥 P를 출력해준다- T가 Promise를 extends하지 않을 때 작동시킬 부분
T extends { then: (onfulfilled: (arg: infer K) => any) => any } ? K : error
- T는 typeT에 적합한 타입인지 확인하고 맞다면 K를 출력, 아니라면 error을 표출한다
쪼개서 생각하니까 간단하다.
type T를 아예 가져다 박아서 해결한 방법이다.
두번째 방법
type MyAwaited2<T extends {then:unknown}> = Awaited<Promise<T>>
이 방법을 통해 Awaited에 대해서 알게 되었다.
T는 항상 {then:unknown}을 extends하고,
Awaited<T>
타입이란?
type Awaited<T> = T extends null | undefined ? T : T extends object & {
then(onfulfilled: infer F, ...args: infer _): any;
} ? F extends (value: infer V, ...args: infer _) => any ? Awaited<...> : never : T
음..?
공식문서를 보자
공식문서
The Awaited Type and Promise Improvements
TypeScript 4.5 introduces a new utility type called the Awaited type. This type is meant to model operations like await in async functions, or the .then() method on Promises - specifically, the way that they recursively unwrap Promises.
// A = string
type A = Awaited<Promise<string>>;
// B = number
type B = Awaited<Promise<Promise<number>>>;
// C = boolean | number
type C = Awaited<boolean | Promise<number>>;
The Awaited type can be helpful for modeling existing APIs, including JavaScript built-ins like Promise.all, Promise.race, etc. In fact, some of the problems around inference with Promise.all served as motivations for Awaited. Here’s an example that fails in TypeScript 4.4 and earlier.
declare function MaybePromise<T>(value: T): T | Promise<T> | PromiseLike<T>;
async function doSomething(): Promise<[number, number]> {
const result = await Promise.all([MaybePromise(100), MaybePromise(200)]);
// Error!
//
// [number | Promise<100>, number | Promise<200>]
//
// is not assignable to type
//
// [number, number]
return result;
}
Now Promise.all leverages the combination of certain features with Awaited to give much better inference results, and the above example works.
For more information, you can read about this change on GitHub.
비동기 함수의 await 또는 Promise의 .then() 메서드, 특히 재귀적으로 Promise를 언래핑하는 방식과 같은 작업을 수행하는 유틸리티 타입이다.
두번째 방법 type MyAwaited2<T extends {then:unknown}> = Awaited<Promise<T>>
이 이렇게 짧은 이유가 여기 있다
+T가 {then:unknown}을 해야 하는 이유는 이를 수행하지 않으면 type T 또한 error가 찍히기 떄문이다
'✍ 공부 > TypeScript' 카테고리의 다른 글
[type-challenges] Concat (0) | 2023.05.23 |
---|---|
[type-challenges] If (0) | 2023.05.23 |
[type-challenges] Exclude (0) | 2023.05.23 |
[type-challenges] Length of Tuple (0) | 2023.05.23 |
[type-challenges] First of Array (0) | 2023.05.23 |