假设我有以下代码:

async promiseOne() {
  return 1
} // => Promise<number>

const promisedOne = promiseOne()

typeof promisedOne // => Promised<number>

我将如何将promise结果的类型(在这个简化的例子中是一个数字)提取为它自己的类型?

推荐答案

打字脚本4.5

Awaited类型现在内置于语言中,因此您无需自己编写.

type T = Awaited<Promise<PromiseLike<number>> // => number

打字脚本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

打字脚本4.5(source)使用的定义比这更复杂,它涵盖了不适用于大多数用例的边缘情况,但它可以应用于任何TypeScript 4.1+项目.

打字脚本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 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

Typescript相关问答推荐

根据第一个参数的值设置第二个参数的类型

Typescript自定义键映射

在Switch陈述中输入保护类

即使子网是公共的,AWS CDK EventBridge ECS任务也无法分配公共IP地址

参数类型undefined不能分配给参数类型字符串|未定义

在类型内部使用泛型类型时,不能使用其他字符串索引

在Angular中注入多个BrowserModules,但在我的代码中只有一个

在嵌套属性中使用Required

PrimeNG日历需要找到覆盖默认Enter键行为的方法

如何使用类型谓词将类型限制为不可赋值的类型?

使用2个泛型类型参数透明地处理函数中的联合类型

正确使用相交类型的打字集

我在Angular 17中的environment.ts文件中得到了一个属性端点错误

打字错误推断

我不明白使用打字脚本在模板中展开参考

如何在Reaction Native中关注屏幕时正确更新状态变量?

为什么数字是`{[key:string]:UNKNOWN;}`的有效密钥?

为什么看起来相似的代码在打字时会产生不同的错误?

Typescript 从泛型推断类型

如何按成员资格排除或 Select 元组?