typescript 在这种情况下,“promise.all”的类型定义如何工作?

zdwk9cvp  于 2022-12-19  发布在  TypeScript
关注(0)|答案(2)|浏览(242)

我在做a question of type-challenges的时候发现了这个问题。
在第3种情况下,以下代码将失败:

declare function PromiseAll<A extends readonly unknown[]>(values: A): Promise<{
  -readonly [key in keyof A]: Awaited<A[key]>
}>

// test
const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)]) 
// except Promise<[number, number, number]>, but got Promise<number[]>

但是当我像下面这样将通用的readonly unknown[]更改为(readonly unknown[]) | []时,一切都会好起来。

declare function PromiseAll<A extends (readonly unknown[]) | [] /* change here */>(values: A): Promise<{
  -readonly [key in keyof A]: Awaited<A[key]>
}>

// test
const promiseAllTest3 = PromiseAll([1, 2, Promise.resolve(3)]) // Promise<[number, number, number]>

我不明白为什么给泛型“A”添加一个约束"[]“会影响案例3。我根本无法将它们关联起来。
此外,正确的实现是lib.es2015.promise.d.ts的标准实现,我在vscode中找到了它

// lib.es2015.promise.d.ts
/**
 * Creates a Promise that is resolved with an array of results when all of the provided Promises
 * resolve, or rejected when any Promise is rejected.
 * @param values An array of Promises.
 * @returns A new Promise.
 */
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;

我发现这段代码很好用。因为它通过转换成元组来保持所有类型和它们的位置:

declare function PromiseAll<A extends any[]>(values: readonly [...A]): Promise<{
  [key in keyof A]: Awaited<A[key]>
}>
n9vozmp4

n9vozmp41#

作为传入函数的数组常量的默认推理行为,TypeScript团队选择了在 tuple 类型上推理 array 类型。
如果将鼠标悬停在函数调用上,则可以在工具提示中看到它:

declare function PromiseAll<A extends readonly unknown[]>(values: A): void

PromiseAll([1, 2, Promise.resolve(3)])
// function PromiseAll<(number | Promise<number>)[]>(...)

A的类型被推断为数组类型(number | Promise<number>)[],而不是元组类型[number, number, Promise<number>]
这种行为在大多数情况下都能正常工作,但是要正确地输入Promise.all()函数,我们 * 需要 * 来推断 tuple 类型。有多种方法可以做到这一点,您在问题中已经发现了其中的两种。
但是为什么这两个方法都要推断元组类型呢?我们可以从ahejlsberg的#31434中得到解释
我们仅在上下文类型是或包含类似元组的类型时才推断元组类型
(readonly unknown[]) | []之所以有效,是因为约束中的并集现在包含了元组类型[],这足以提示编译器推断元组类型。
使用readonly [...A]也会推断元组,因为它使用的是4.0中引入的可变元组语法。有关可变元组类型推断的详细说明,请参见PR/#39094
当数组文字的上下文类型是元组类型时,为数组文字推断元组类型。类型[...T]可方便地用于指示元组类型的推断的首选项,其中T是类似数组的类型参数。

z9gpfhce

z9gpfhce2#

基本上,它是窄元组[number, number, number]类型,而不是宽数组number[],因为将... | []添加到该类型,将文本类型[]添加到定义返回类型的上下文,在本例中添加到泛型A
这是详细解释/实施所发生事件的PR:GitHub
并且您的代码是PR中引入的以下更改的完美示例:
除非元素具有包含文本类型的上下文类型,否则为数组文本中的元素推断的类型是表达式的加宽文本类型。
PromiseAll的返回类型是在A的上下文中定义的。A又在其定义中使用了文本类型。因此,它构造了一个“包含文本类型的上下文类型”,以防止类型从元组扩展到数组。

相关问题