有这样一段TS代码
type Awaited<T> = T extends PromiseLike<infer P> ? P extends PromiseLike<any> ? Awaited<P> : P : never;
看起来有点绕,接下来我们详细节讲解一下它的使用和应用场景
1. 整体类型定义结构
这是一个自定义的条件类型(Conditional Type)定义,名为 Awaited
,用于处理 Promise
或者实现了 PromiseLike
接口的类型,目的是获取这个 Promise
(或类似 Promise
的类型)最终被 await
之后解析出来的值的类型。
2. 条件判断部分(T extends PromiseLike<infer P>
)
PromiseLike
接口:在 TypeScript 中,PromiseLike
是一个表示类似Promise
的类型的接口。只要一个类型具有then
方法(就像标准的Promise
那样,其then
方法用于添加成功和失败的回调),就可以认为它是PromiseLike
类型。infer
关键字:infer
用于在条件类型中做类型推断。在这里,infer P
的作用是从满足T extends PromiseLike<...>
这个条件的类型T
中,尝试推断出Promise
所包含的内部值的类型,并将其命名为P
。例如,如果T
是Promise<number>
,那么通过infer
就能推断出P
为number
。
3. 嵌套条件判断(P extends PromiseLike<any>
)
- 进一步判断通过
infer
推断出来的P
类型是否本身也是一个PromiseLike
类型。这是因为在 JavaScript(以及 TypeScript 遵循其异步模型)中,有可能存在多层嵌套的Promise
,比如Promise<Promise<number>>
这种情况。 - 如果
P
仍然是PromiseLike
类型,那就需要继续递归地去解析它最终包含的值的类型,所以再次使用Awaited<P>
进行递归调用,不断解开嵌套的Promise
,直到最终解析出的类型不再是PromiseLike
类型为止。
4. 返回值部分
P
作为返回值:如果经过判断,P
不是PromiseLike
类型了(也就是已经是最内层的实际值的类型了),那么就直接返回P
,这个P
就是当前这个Promise
(或者PromiseLike
类型)被await
之后会得到的值的类型。never
作为返回值:如果一开始的T
类型根本就不满足PromiseLike
类型的条件(也就是不扩展自PromiseLike
),那么整个条件类型就返回never
,表示不存在这样一个可被await
解析出有效类型的值。
5. 应用场景示例
假设有如下的异步函数返回类型:
async function getData(): Promise<Promise<string>> {
return Promise.resolve('Hello');
}
这里 getData
函数返回的是一个嵌套的 Promise
(外层 Promise
包裹着内层的 Promise<string>
)。
如果我们想要获取最终 await
这个函数返回值之后实际得到的值的类型,可以使用 Awaited
类型来进行推导:
type ResultType = Awaited<ReturnType<typeof getData>>;
// ResultType 的类型会被推导为 string,因为 Awaited 类型会解析掉嵌套的 Promise,得到最终的实际值类型
这样的类型定义在处理复杂的异步操作、对异步返回值进行类型安全的处理以及构建通用的异步相关的类型工具等场景中非常有用,可以帮助开发者更精准地把握代码中的类型关系,避免因异步类型处理不当而出现的类型错误。