假设我有以下代码:
async promiseOne() {
return 1
} // => Promise<number>
const promisedOne = promiseOne()
typeof promisedOne // => Promised<number>
我将如何将promise结果的类型(在这个简化的例子中是一个数字)提取为它自己的类型?
假设我有以下代码:
async promiseOne() {
return 1
} // => Promise<number>
const promisedOne = promiseOne()
typeof promisedOne // => Promised<number>
我将如何将promise结果的类型(在这个简化的例子中是一个数字)提取为它自己的类型?
Awaited
类型现在内置于语言中,因此您无需自己编写.
type T = Awaited<Promise<PromiseLike<number>> // => number
您可以通过几种可能的定义自己实现这一点.最简单的是:
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
打字脚本4.5(source)使用的定义比这更复杂,它涵盖了不适用于大多数用例的边缘情况,但它可以应用于任何TypeScript 4.1+项目.
在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
官方不支持这一点,但在实践中,这是完全正确的.
在TypeScript 2.8之前,这是不可能直接实现的.在没有2.8中引入的条件类型的情况下,展开类似promise 的类型需要泛型类型在对象上可用,这样就可以使用索引访问类型来获取值.
如果我们将类型的范围限制为单个级别的promise ,并且只接受promise ,那么在2.8中,通过使用declaration merging向global Promise<T>
接口添加属性,从技术上讲是可以做到这一点的.
interface Promise<T> {
__promiseValue: T
}
type Awaited<T extends Promise<any>> = T["__promiseValue"]
type T = Awaited<Promise<string>> // => string