TypeScript: Promise.all doesn't handle union types

泪湿孤枕 提交于 2021-01-02 06:32:49

问题


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 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)[]



回答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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!