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 on any produces Promise<Record<string, any>> #46169

Closed
DanielRosenwasser opened this issue Oct 2, 2021 · 3 comments · Fixed by #46218
Closed

Promise.all on any produces Promise<Record<string, any>> #46169

DanielRosenwasser opened this issue Oct 2, 2021 · 3 comments · Fixed by #46218
Assignees
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue Fixed A PR has been merged for this issue

Comments

@DanielRosenwasser
Copy link
Member

Found in a large internal codebase:

async function foo(x: any) {
    let abc = await Promise.all(x);
    let oops: any[] = abc;
//      ~~~~
// error: Type '{ [x: string]: any; }' is missing the following properties from type 'any[]': length, pop, push, concat, and 26 more.
}

Promise.all always produces an Array, so this behavior is undesirable.

What's happening if you can't tell is that any is a valid substitution for T extends unknown[]. Upon instantiation, we feed any through a mapped type which acts differently depending on if we've passed in an array or something else.

@DanielRosenwasser
Copy link
Member Author

DanielRosenwasser commented Oct 2, 2021

When instantiating with any, we could instead choose an array-like type when we encounter a deferred homomorphic mapped type that's bounded by an array-like type.

In other words Foo<any> would produce different results (e.g. any[]) compared to than Bar<any> (i.e. Record<string, any>) here:

type Foo<T extends unknown[]> = { [K in keyof T]: T[K] };

type Bar<T extends unknown> = { [K in keyof T]: T[K] };

That wouldn't always work, and could result in behavior that progressively changes every time we instantiate a mapped object type, but I think that's probably usually desirable anyway, and it would work here.

@DanielRosenwasser
Copy link
Member Author

Probably something similar to this in instantiateMappedType

const constraint = getConstraintOfTypeParameter(typeVariable);
if ((t.flags & TypeFlags.Any) && (isArrayType(constraint) || isTupleType(constraint))) {
    return instantiateMappedArrayType(t, type, prependTypeMapping(typeVariable, t, mapper));
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug A bug in TypeScript Fix Available A PR has been opened for this issue Fixed A PR has been merged for this issue
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants