suppose I have some function like:
function allPromises<T>(array: Promise<T>[]): Promise<T[]> {
return Promise.all(array);
}
(note: this is intentionally contrived to be minimally reproducible)
If I did this, it works fine:
const p1: Promise<string> = new Promise((resolve) => resolve('1'));
const p2: Promise<string> = new Promise((resolve) => resolve('2'));
const test1 = [p1, p2];
const res1 = allPromises(test1);
no type errors and res1
is properly typed as Promise<string[]>
;
but if I do:
const p1: Promise<string> = new Promise((resolve) => resolve('1'));
const p2: Promise<number> = new Promise((resolve) => resolve(2));
const test2 = [p1, p2];
const res2 = allPromises(test2);
now I get a TS complaint that:
Argument of type '(Promise<string> | Promise<number>)[]' is not assignable to parameter of type 'Promise<string>[]'.
It seems to be because test2
is typed as (Promise<number> | Promise<string>)[]
I can fix this by doing:
const test2: Promise<string | number>[] = [p1, p2];
then res2
is typed properly as Promise<(string | number)[]>
but is there a way to type my function so that it can infer the type properly and not force me to declare the type of the array explicitly in this way?
Note: I also tried overloading the function similar to how the Promise.all
typing is done, but it also failed and required me to explicitly type the array for TS to accept it:
function allPromises<T1, T2>(array: [Promise<T1>, Promise<T2>]): Promise<[T1, T2]>
function allPromises<T>(array: Promise<T>[]): Promise<T[]>
function allPromises(array: Promise<unknown>[]): Promise<unknown[]> {
return Promise.all(array);
}
and this overload also didn't help:
function allPromises<T1, T2>(array: (Promise<T1> | Promise<T2>)[]): Promise<(T1 | T2)[]>
and I'm looking to make this work in TS V3 ... I see some new types in V4.5 can be useful for this, but I am stuck in V3 for the time being.
CodePudding user response:
I made it work with
type UnwrapPromiseArr<T> = T extends Promise<infer R>[]
? Promise<R[]>
: never;
function allPromises<T extends Promise<unknown>[]>(array: T): UnwrapPromiseArr<T> {
return Promise.all(array) as UnwrapPromiseArr<T>;
}
They key is to infer the union types that initially come from the inner Array.
CodePudding user response:
Your function is just doing the same thing as Promise.all
, so just define it as an alias and defer the great typing to the original function's:
const allPromises = Promise.all;
const p1: Promise<string> = new Promise((resolve) => resolve('1'));
const p2: Promise<number> = new Promise((resolve) => resolve(2));
const test2 = [p1, p2];
const res2 = allPromises(test2);