5

I have a problem with Promises in Typescript. I'm trying to call Promise.all with an array of promises but instead, I get

No overload matches this call. The last overload gave the following error. Argument of type '(Promise | Promise)[]' is not assignable to parameter of type 'Iterable<boolean | PromiseLike>'. [...]

Below a simplified example of I'm referring to:

const promise1 = Promise.resolve(true);         //promise1:Promise<boolean>
const promise2 = Promise.resolve('promise...'); //promise2:Promise<string>
const promises = [promise1, promise2];

Promise.all(promises); //Error here

However, the code below works as I'd assume.:

const promise1 = Promise.resolve(true);
const promise2 = Promise.resolve('promise...');

Promise.all([promise1, promise2]);

Seems like a bug to me, but I'm not sure. I've been looking through GitHub, searching this problem but I didn't find any matching. Or am I doing something wrong and there is a solution so you can overcome the problem. Obviously, I can't just write Promise.all([promise1, promise2]) in my primary code because the array of promises is dynamic.

I'm using typescript @3.9.6.

Dominik
  • 75
  • 2
  • 6
  • 1
    Not an answer, because I can't explain the reason why it's not working automatically. But a simple solution would be to help the compiler and tell him the correct type like so `const promises: Promise[] = [promise1, promise2];` – Christoph Lütjen Jul 14 '20 at 12:22
  • 1
    That's probably because TypeScript cannot infer the `promises` array as an iterable of promises, but an array of a union type, which causes the mismatch in typing. Like what Christoph has suggested, you can simply cast `promises` so that it has a type of `Promise[]`, just as a hint to TypeScript. – Terry Jul 14 '20 at 12:32
  • Thank you for your comments but the problem is that the array of promises isn't fixed size. Original code that the question is based on is used to handle database queries. So the number of promises in the array may increase over time and it can contain a lot of different types. – Dominik Jul 14 '20 at 12:50
  • If you don't want to list possible return types, just use `any` as Terry wrote in his comment. – Christoph Lütjen Jul 14 '20 at 12:57
  • Your example originally [did work](https://stackoverflow.com/questions/60758372/typescript-promise-all-doesnt-handle-union-types) with a [dev version of TS 3.9](https://www.typescriptlang.org/play/?ts=3.9.0-dev.20200323#code/MYewdgzgLgBADgJxAWwJYQKYEYYF4YAKSamAdAhhCADYBuGAFFAgK4YCUA3DD77wPT9EKdNgBcREZgA8AIxA0MAQzAA+ALAAoUJFjCSGAEx5CxUeUo16DAOT7zpUja4xB9zIYlmZ0BKjAA5hra4NDw3pQmANru2AA04VJGALqcWlqSBqRK1NQMsRAuggCiCEgIMAAWGBRAA). Unfortunately, `awaited` has been [reverted](https://github.com/microsoft/TypeScript/pull/37610), so we'll have to wait a bit longer and use one of the workarounds. – ford04 Jul 14 '20 at 14:05

1 Answers1

8

The problem has nothing to do with Promises per se, but rather with the way TypeScript infers the type of a tuple.

You can read more about the actual issue in this answer, but the gist of it is the following

const a = [1, "a", true] //<- (string | number | boolean)[], not [number, string, boolean]

The problem should be fixed with the introduction of variadic kinds in TS 4.0. For now, there are two ways to do what you want.

The first one is const assertions

const b = [1, "a", true] as const // <- [number, string, boolean]

And the second one is to use a utility function like the one posted in the answer I linked above.


In your specific case, the way Promise.all is typed doesn't play well with how TS infers the type of promises. This is easily fixed by having a proper tuple with one of the methods described above.

Playground link

bugs
  • 14,631
  • 5
  • 48
  • 52