问题
I have the following piece of code that results in compile error in TypeScript:
type Promisable = (() => Promise<string>) | (() => Promise<number>);
const func = async (promisable: Promisable) => {
await Promise.all([promisable()]);
};
The error is as follows
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>'. The types returned by 'Symbol.iterator.next(...)' are incompatible between these types.
For the record, removing the union type works as intended:
type Promisable = () => Promise<string>;
const func = async (promisable: Promisable) => {
await Promise.all([promisable()]);
};
You can see the error for yourself here https://www.typescriptlang.org/play/?ssl=4&ssc=3&pln=1&pc=1#code/C4TwDgpgBACgTgewLYEsDOBDARgG2gXigAoiBKKfAPlkVTQgB41g4UA7Ac0vIB9iyK1eMnSM2AVyRYIcbgG4AsACgAxgjbMoAM3FsVFKBjQg9xMLXTY8ALhojMuCOSpQA3sqiGA7hhTA7dBAAdBg4OEQA2ub2VhBkALqkikoAvnJAA
Is it not possible to use union types in combination with Promise.all
?
EDIT:
I know it's possible to use something like () => Promise<string|number>
instead. But in an advanced application with a lot of asynchronous functions and big types, it is not easy to convert union of functions into function of union. It's not very practical from the code perspective as well.
回答1:
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 likePromise.all
,Promise.race
,Promise.allSettled
,Promise.prototype.then
, andPromise.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)[]
回答2:
You don't need such a verbose type, this will do:
type Promisable = (() => Promise<string|number>);
const func = async (promisable: Promisable) => {
await Promise.all([promisable()]);
};
来源:https://stackoverflow.com/questions/60758372/typescript-promise-all-doesnt-handle-union-types