While working on a question from type-challenges repository, I encountered this issue.
The code below fails in case 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)])
// expected Promise<[number, number, number]>, but got Promise<number[]>
However, simply changing the generic readonly unknown[]
to (readonly unknown[]) | []
fixes the issue:
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]>
The reason why adding a constraint of '[]' to generic 'A' affects case 3 is unclear to me. The connection between them is not obvious.
Furthermore, I discovered that the correct implementation is found in lib.es2015.promise.d.ts, which is provided in 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]> }>;
Upon testing, I found that this code works perfectly. By converting to Tuple, it maintains all types and their positions:
declare function PromiseAll<A extends any[]>(values: readonly [...A]): Promise<{
[key in keyof A]: Awaited<A[key]>
}>