TypeScript 4.5
Awaited 类型现在已内置到语言中,因此您无需自己编写。
type T = Awaited<Promise<PromiseLike<number>> // => number
TypeScript 4.1 到 4.4
您可以自己实现它,有几个可能的定义。最简单的是:
type Awaited<T> = T extends PromiseLike<infer U> ? U : T
// Awaited<Promise<number>> = number
请注意,此类型使用PromiseLike 而不是Promise。这对于正确处理用户定义的等待对象很重要。
这使用conditional type 来检查T 是否看起来像一个promise,如果是则打开它。但是,这将不正确地处理Promise<Promise<string>>,将其解包到Promise<string>。等待一个 Promise 永远不会给出第二个 Promise,因此更好的定义是递归地解开 Promise。
type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T
// Awaited<Promise<Promise<number>>> = number
TypeScript 4.5 (source) 使用的定义比这更复杂,以涵盖不适用于大多数用例的边缘情况,但它可以放入任何 TypeScript 4.1+ 项目中。
TypeScript 2.8 到 4.0
在 TypeScript 4.1 之前,该语言没有有意支持递归类型别名。上面显示的简单版本仍然可以工作,但递归解决方案需要一个额外的对象类型来欺骗编译器认为该类型不是递归的,然后使用indexed access type 提取我们想要的属性。
type Awaited<T> = T extends PromiseLike<infer U>
? { 0: Awaited<U>; 1: U }[U extends PromiseLike<any> ? 0 : 1]
: T
官方不支持,但实际上完全没问题。
2.8 之前的 TypeScript
在 TypeScript 2.8 之前,这是不可能的。在没有 2.8 中引入的条件类型的情况下展开类似 Promise 的类型将要求泛型类型在对象上可用,以便可以使用索引访问类型来获取值。
如果我们将类型的范围限制为单级承诺,并且只接受承诺,那么在 2.8 中通过使用declaration merging 向全局Promise<T> 接口添加一个属性在技术上是可行的。
interface Promise<T> {
__promiseValue: T
}
type Awaited<T extends Promise<any>> = T["__promiseValue"]
type T = Awaited<Promise<string>> // => string