TypeScript: Promise.all doesn't handle union types

Update

This is one of the cases, where type inference with current promise type declarations fails. Simplest solution is to just add the generic type argument manually:

const promisable: Promisable = ...
const res = await Promise.all<string | number>([promisable()]); 
// res: (string|number)[]

You might infer string | number automatically:

type PromiseReturn<T> = T extends () => Promise<infer I> ? I : never
const res = await Promise.all<PromiseReturn<Promisable>>([promisable()]);

With TypeScript 4.1: More complex, potentially nested Promise types can be resolved and flattened with a custom recursive Awaited type like this:

type Awaited<T> = T extends PromiseLike<infer U> ? Awaited<U> : T;

Playground



Old answer

Update: The awaited type operator is delayed to later versions - not clear whether it will be released at all.


This is a known issue. Good news: TS 3.9 (beta soon) will come out with improved promise types:

I would like to reintroduce the awaited type operator from #17077 to meet our needs for a mechanism to recursively unwrap a Promise-like type for methods like Promise.all, Promise.race, Promise.allSettled, Promise.prototype.then, and Promise.prototype.catch.

Type declarations of Promise.all and others use the new awaited type operator. If you test with the nightly build, Promise.all now correctly resolves to Promise<(string | number)[]>:

type Promisable = (() => Promise<string>) | (() => Promise<number>);

declare const promisable: Promisable
const res = await Promise.all([promisable()]); // res: (string | number)[]

In contrast, TS 3.8 can't handle it. For versions < 3.9, you can manually assign the generic type argument:

declare const promisable: Promisable
const res = await Promise.all<string | number>([promisable()]); // res: (string | number)[]