Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Promise.all and Promise.allSettled infer incorrect types from Array subclasses #51993

Open
asnaeb opened this issue Dec 22, 2022 · 4 comments
Open
Labels
Bug A bug in TypeScript
Milestone

Comments

@asnaeb
Copy link

asnaeb commented Dec 22, 2022

lib Update Request

Configuration Check

My compilation target is ES2020 and my lib is the default.

Missing / Incorrect Definition

Promise.all
Promise.allSettled

Sample Code

declare class ArrayChild<T> extends Array<T> {}

declare const array: Array<Promise<number>>
declare const child: ArrayChild<Promise<number>>

Promise.all(array).then(a => a /* number[] */) 
Promise.allSettled(array).then(a => a /* PromiseSettledResult<number>[] */)

Promise.all(child).then(a => a /* {[K in keyof ArrayChild]: number} */) 
Promise.allSettled(child).then(a => /* {[K in keyof ArrayChild]: PromiseSettledResult<number>} */) 

Playground link

Documentation Link

Promise.all
Promise.allSettled

Possible fix

The inferred value should be of type number[] exactly like what happens with normal Array class. In the linked playground, It can be seen that Promise.all and Promise.allSettled infer the correct type with an Iterable type of object. This undesired behavior just happens with Array subclasses. I have quickly tried changing the signature from the original:

allSettled<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>> }>;
all<T extends readonly unknown[] | []>(values: T): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>;

to

allSettled<T extends Promise<unknown>[]>(values: T): Promise<T extends Promise<infer S>[] ? PromiseSettledResult<S>[] : never>
all<T extends Promise<unknown>[]>(values: T): Promise<T extends Promise<infer S>[] ? S[] : never>

and it works as expected: Playground link

@IllusionMH
Copy link
Contributor

IllusionMH commented Dec 22, 2022

This would break tuples, which (I assume), are more common use case than Array subclasses in promise methods.

Example

@andrewbranch andrewbranch added the Bug A bug in TypeScript label Dec 22, 2022
@andrewbranch andrewbranch added this to the Backlog milestone Dec 22, 2022
@andrewbranch
Copy link
Member

Yeah, we definitely can’t break tuple inference here. Seems like there could be another way to write it, but we’ll have to weigh the complexity and performance cost against how useful it actually is on average in real code.

@asnaeb
Copy link
Author

asnaeb commented Dec 22, 2022

Extending Array class, while uncommon is part of the language and this bug can be a wall against libraries development, for example. I'm going to spend some time in finding a solution as soon as I get the chance!

@asnaeb asnaeb changed the title Promise.all and Promise.allSettled infer incorrect types from Array child classes Promise.all and Promise.allSettled infer incorrect types from Array subclasses Dec 22, 2022
@asnaeb
Copy link
Author

asnaeb commented Dec 25, 2022

While I didn't test this exhaustively, It looks like it's working

declare function all<T extends [unknown] | ArrayLike<unknown> | Iterable<unknown>>(values: T): Promise<
    T extends {[P in 0]: unknown} ? 
        {-readonly [P in keyof T]: Awaited<T[P]>} : 
            T extends ArrayLike<infer S> | Iterable<infer S> ? Awaited<S>[] : never 
>

Playground Link

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants