How to use Promise.all() with Typescript

Its generally best to have arrays with consistent types. You can do the following manually though (passing in generic arguments):

Promise.all<Aurelia, void>(
  [aurelia.start(), entityManagerProvider.initialize()
])
.then(results => {
    let aurelia = results[0];
    aurelia.setRoot();
});

Since Promise::all is a generic function, you can declare the return types of each promise like this:

Promise.all<Aurelia, void>([
  aurelia.start(),
  entityManagerProvider.initialize()
])
.then(([aurelia]) => aurelia.setRoot());

At least from TypeScript 2.7.1 onwards, the compiler seems to resolve the types without help, with a syntax like this:

Promise.all([fooPromise, barPromise]).then(([foo, bar]) => {
  // compiler correctly warns if someField not found from foo's type
  console.log(foo.someField);
});

Hat tip: @JamieBirch (from comment to @AndrewKirkegaard's answer)


If you'd like to keep type-safety, it's possible to extend the native type-definition of the Promise object (of type PromiseConstructor) with additional overload signatures for when Promise.all is called with a finite number of not-necessarily inter-assignable values:

interface PromiseConstructor
{
    all<T1, T2>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>]): Promise<[T1, T2]>;
    all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;
    ...
}

Add as many overloads as you need. This approach provides full type-safety for all elements in the value argument of the onfulfilled callback:

Promise.all([1, "string", true]).then(value =>
{
    let a: number = value[0]; // OK
    let b: number = value[1]; // Type 'string' is not assignable to type 'number'.
    ...
});

PART 1

There are functions you need to understand A) Promise.all and B) Promise.then:

A) the type definition of Promise.all is a function:

    all<T>(values: readonly (T | PromiseLike<T>)[]): Promise<T[]>;

B) the type definition of Promise.then is a function that is a bit more complex:

    then<TResult1 = T, TResult2 = never>(onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | undefined | null, onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | undefined | null): Promise<TResult1 | TResult2>;

PART 1.a

Promise.then's type definition is a lot but it can be broken down into small parts:

then<TResult1 = T, TResult2 = never> a function then with 2 generics TResult1, TResult2. The < > means we can set and use values inside them later - they are called generics.

The then function itself: (onfulfilled?: ..., onrejected?: ...): Promise<TResult1 | TResult2>.

PromiseLike is a helper type and the same as Promise (for an intro lesson).

onfulfiled and onrejected are functions in the form of: (value: T) => (TResult1 OR PromiseLike<TResult1>) OR undefined OR null. Notice the generic T is used here.

PART 2 -

Promise itself has a generic interface: interface Promise<T>. The <T> is a/the generic.

So when you call

Promise.all<SomeCoolType>([a(), b(), c()]).then( value => doSomething(value) )

your generic is SomeCoolType and in this example some cool type is

interface SomeCoolType = [A() => string, B() => boolean, C() => number]

Now remember that A B C have to be Promises. And this makes it so your value in .then( value => ... is going to the result of SomeCoolType which for us is calling all those functions, the result is [string, boolean, number].

CONCLUSION -

Concretely, the array of function/promises you pass into you Promise.all<T> are generics that are used in .then(result => ...). The return/resolve value of those promises will become the value/type of result.

Example: Promise.all<[Promise<() => string>]>([returnStringAsync()]).then(result => console.log(typeof result === "string")); # => true