简体   繁体   中英

How does the type defination of `promise.all` work well in this case?

I found this problem when I was doing a question of type-challenges .

The following code will fail in the 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)]) 
// except Promise<[number, number, number]>, but got Promise<number[]>

But when I change the generic readonly unknown[] to (readonly unknown[]) | [] (readonly unknown[]) | [] like below,everything will be ok.

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]>

I don't understand why adding a constraint of '[]' to generic 'A' will affect the case 3. I can't associate them at all.

In addition, the correct implementation is the standard implementation of lib.es2015.promise.d.ts, which I found 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]> }>;

I found this code is work fine. Because it keeps all types and their positions by converting to Tuple:

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

Basically its the narrow tuple [number, number, number] type instead of the widened array number[] because adding ... | [] ... | [] to the type, adds a literal type [] to the context in which in the return type is defined, in this case to the Generic A .

This is the PR that explains/implements whats happening in detail: GitHub

and your code is a perfect example for the following change introduced in the PR:

The type inferred for an element in an array literal is the widened literal type of the expression unless the element has a contextual type that includes literal types.

The return type of PromiseAll is defined in the context of A . A in turn uses a literal type in its definition. therefore it consitutes a "contextual type that includes literal types" preventing the typewidening from tuple to array.

As the default inference behaviour for array literals passed into a function, the TypeScript team has choosen to infer array types over tuple types.

You can see it in the tooltip if you hover over the function call:

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

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

The type of A is inferred as the array type (number | Promise<number>)[] instead of the tuple type [number, number, Promise<number>] .

This behaviour works fine in most situations, but to type a Promise.all() function properly, we need to infer the tuple type. There are multiple ways to do this and you have discovered two of them in your question.


But why exactly do both of these methods infer the tuple type? We can get clarification from ahejlsberg in #31434

We only infer tuple types when the contextual type is or contains a tuple-like type

And this is pretty much the important point. (readonly unknown[]) | [] (readonly unknown[]) | [] works because the union in the constraint now contains a tuple type [] . This is enough to hint at the compiler to infer a tuple type.

Using readonly [...A] also infers the tuple since it is using the variadic tuple syntax introduced in 4.0 . A detailed explanation about inference with variadic tuple types can be found at PR/#39094 .

When the contextual type of an array literal is a tuple type, a tuple type is inferred for the array literal. The type [...T], where T is an array-like type parameter, can conveniently be used to indicate a preference for inference of tuple types.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM