我在做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]>
}>
2条答案
按热度按时间n9vozmp41#
作为传入函数的数组常量的默认推理行为,TypeScript团队选择了在 tuple 类型上推理 array 类型。
如果将鼠标悬停在函数调用上,则可以在工具提示中看到它:
A
的类型被推断为数组类型(number | Promise<number>)[]
,而不是元组类型[number, number, Promise<number>]
。这种行为在大多数情况下都能正常工作,但是要正确地输入
Promise.all()
函数,我们 * 需要 * 来推断 tuple 类型。有多种方法可以做到这一点,您在问题中已经发现了其中的两种。但是为什么这两个方法都要推断元组类型呢?我们可以从ahejlsberg的#31434中得到解释
我们仅在上下文类型是或包含类似元组的类型时才推断元组类型
(readonly unknown[]) | []
之所以有效,是因为约束中的并集现在包含了元组类型[]
,这足以提示编译器推断元组类型。使用
readonly [...A]
也会推断元组,因为它使用的是4.0中引入的可变元组语法。有关可变元组类型推断的详细说明,请参见PR/#39094。当数组文字的上下文类型是元组类型时,为数组文字推断元组类型。类型[...T]可方便地用于指示元组类型的推断的首选项,其中T是类似数组的类型参数。
z9gpfhce2#
基本上,它是窄元组
[number, number, number]
类型,而不是宽数组number[]
,因为将... | []
添加到该类型,将文本类型[]
添加到定义返回类型的上下文,在本例中添加到泛型A
。这是详细解释/实施所发生事件的PR:GitHub
并且您的代码是PR中引入的以下更改的完美示例:
除非元素具有包含文本类型的上下文类型,否则为数组文本中的元素推断的类型是表达式的加宽文本类型。
PromiseAll
的返回类型是在A
的上下文中定义的。A
又在其定义中使用了文本类型。因此,它构造了一个“包含文本类型的上下文类型”,以防止类型从元组扩展到数组。