Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

v3.7.2 Promise.all (No overload matchs this call) with different promise types and more than 10 promises #35617

Closed
WandersonAlves opened this issue Dec 10, 2019 · 8 comments

Comments

@WandersonAlves
Copy link

TypeScript Version: v3.7.2

Search Terms:
Promise.all, No overload matchs this call
Expected behavior:
Use a variable number of promises in a Promise.all call
Actual behavior:
If we use more than 10 promises, and these promises return different types, the compiler throws a error

Related Issues:

Code

// This works!
(async () => {
    const a0: Promise<number> = Promise.resolve(0);
    const a1: Promise<string> = Promise.resolve('0');
    const a2: Promise<number> = Promise.resolve(0);
    const a3: Promise<number> = Promise.resolve(0);
    const a4: Promise<number> = Promise.resolve(0);
    const a5: Promise<number> = Promise.resolve(0);
    const a6: Promise<number> = Promise.resolve(0);
    const a7: Promise<number> = Promise.resolve(0);
    const a8: Promise<number> = Promise.resolve(0);
    const a9: Promise<number> = Promise.resolve(0);
    const result = await Promise.all([a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]);
})();
// If we use more than 10 promises with diferent types in a Promise.all, Promise.all throws a error
/**
 * No overload matches this call.
  The last overload gave the following error.
    Argument of type '(Promise<number> | Promise<string>)[]' is not assignable to parameter of type 'Iterable<number | PromiseLike<number>>'.
      The types returned by '[Symbol.iterator]().next(...)' are incompatible between these types.
        Type 'IteratorResult<Promise<number> | Promise<string>, any>' is not assignable to type 'IteratorResult<number | PromiseLike<number>, any>'.
          Type 'IteratorYieldResult<Promise<number> | Promise<string>>' is not assignable to type 'IteratorResult<number | PromiseLike<number>, any>'.
            Type 'IteratorYieldResult<Promise<number> | Promise<string>>' is not assignable to type 'IteratorYieldResult<number | PromiseLike<number>>'.
              Type 'Promise<number> | Promise<string>' is not assignable to type 'number | PromiseLike<number>'.
                Type 'Promise<string>' is not assignable to type 'number | PromiseLike<number>'.
                  Type 'Promise<string>' is not assignable to type 'PromiseLike<number>'.
                    Types of property 'then' are incompatible.
                      Type '<TResult1 = string, TResult2 = never>(onfulfilled?: ((value: string) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => Promise<...>' is not assignable to type '<TResult1 = number, TResult2 = never>(onfulfilled?: ((value: number) => TResult1 | PromiseLike<TResult1>) | null | undefined, onrejected?: ((reason: any) => TResult2 | PromiseLike<...>) | null | undefined) => PromiseLike<...>'.
                        Types of parameters 'onfulfilled' and 'onfulfilled' are incompatible.
                          Types of parameters 'value' and 'value' are incompatible.
                            Type 'string' is not assignable to type 'number'.(2769)
input.ts(29, 6): The last overload is declared here.
 */
(async () => {
    const a0: Promise<number> = Promise.resolve(0);
    const a1: Promise<string> = Promise.resolve('0');
    const a2: Promise<number> = Promise.resolve(0);
    const a3: Promise<number> = Promise.resolve(0);
    const a4: Promise<number> = Promise.resolve(0);
    const a5: Promise<number> = Promise.resolve(0);
    const a6: Promise<number> = Promise.resolve(0);
    const a7: Promise<number> = Promise.resolve(0);
    const a8: Promise<number> = Promise.resolve(0);
    const a9: Promise<number> = Promise.resolve(0);
    const a10: Promise<number> = Promise.resolve(0);
    const result = await Promise.all([a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]);
})();
Output
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
    return new (P || (P = Promise))(function (resolve, reject) {
        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
        step((generator = generator.apply(thisArg, _arguments || [])).next());
    });
};
// This works!
(() => __awaiter(void 0, void 0, void 0, function* () {
    const a0 = Promise.resolve(0);
    const a1 = Promise.resolve('0');
    const a2 = Promise.resolve(0);
    const a3 = Promise.resolve(0);
    const a4 = Promise.resolve(0);
    const a5 = Promise.resolve(0);
    const a6 = Promise.resolve(0);
    const a7 = Promise.resolve(0);
    const a8 = Promise.resolve(0);
    const a9 = Promise.resolve(0);
    const result = yield Promise.all([a0, a1, a2, a3, a4, a5, a6, a7, a8, a9]);
}))();
// If we use more than 10 promises with diferent types in a Promise.all, Promise.all throws a error
(() => __awaiter(void 0, void 0, void 0, function* () {
    const a0 = Promise.resolve(0);
    const a1 = Promise.resolve('0');
    const a2 = Promise.resolve(0);
    const a3 = Promise.resolve(0);
    const a4 = Promise.resolve(0);
    const a5 = Promise.resolve(0);
    const a6 = Promise.resolve(0);
    const a7 = Promise.resolve(0);
    const a8 = Promise.resolve(0);
    const a9 = Promise.resolve(0);
    const a10 = Promise.resolve(0);
    const result = yield Promise.all([a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10]);
}))();
Compiler Options
{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "types": ["reflect-metadata", "swagger-express-ts"],
    "outDir": "dist",
    "moduleResolution": "node",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "inlineSourceMap": true,
    "lib": ["es2017"]
  }
}

Playground Link: Provided

@WandersonAlves
Copy link
Author

Real project prints:

Working:
image

Error:
image

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Dec 10, 2019

You can probably use declaration merging to implement your own promise.all with 11,12,13+-tuples

@MartinJohns
Copy link
Contributor

A month later: "No overload with more than 150 promises"

@AnyhowStep
Copy link
Contributor

AnyhowStep commented Dec 10, 2019

@MartinJohns I honestly had that problem for quite a bit, before tuple types+rest args were introduced.

I ended up writing code generators that would generate N overloads for me; one for each number of arguments.

Sometimes, it would cause compile times to slow and I would have to reduce the value of N.

It was always a struggle between supporting edge cases with high N values and having sane declaration emit/compile times.

Even now, I still have problems with it, sometimes, but for trampoline types. Where I support N iterations of a recursive type and have a comment that says, "If you need > N iterations, you're doing something very suspicious"

@MartinJohns
Copy link
Contributor

@AnyhowStep six months later: "No overload with more than 2147483647 promises"

In all seriousness, it's always a compromise, and declaration merging and custom overloads seem like the best option for such specific corner cases.

@fatcerberus
Copy link

Anyone literally passing in two billion promises to Promise.all either has a nasty bug in their code or else is doing something very, very wrong. 😉

But yeah, it’s frustrating when you can’t easily write a single type to handle “arbitrary number of Xs” and have to resort to overloads or the like. That’s why I love changes like the recursive type aliases PR that enabled JSON-like types.

@Jamesernator
Copy link

Jamesernator commented Dec 11, 2019

But yeah, it’s frustrating when you can’t easily write a single type to handle “arbitrary number of Xs” and have to resort to overloads or the like.

Actually you can: #27179

For Promise.all:

type UnwrapPromise<T> = T extends Promise<infer R> ? R : T;

declare function PromiseAll<T extends Array<any> | [any]>(
    promises: T,
): Promise<{ [K in keyof T]: UnwrapPromise<T[K]> }>;

// a infered as Promise<[number, string]>
const a = PromiseAll([
    Promise.resolve(3),
    Promise.resolve('foo'),
]);

// b infered as Promise<[0,1,2,3,4,5,6,7,8,9,10,11,12,13]>
const b = PromiseAll([
    Promise.resolve(0 as const),
    Promise.resolve(1 as const),
    Promise.resolve(2 as const),
    Promise.resolve(3 as const),
    Promise.resolve(4 as const),
    Promise.resolve(5 as const),
    Promise.resolve(6 as const),
    Promise.resolve(7 as const),
    Promise.resolve(8 as const),
    Promise.resolve(9 as const),
    Promise.resolve(10 as const),
    Promise.resolve(11 as const),
    Promise.resolve(12 as const),
    Promise.resolve(13 as const),
]);

const l: Array<string | number> = [];

// c infered as Promise<Array<string | number>>
const c = PromiseAll(l);

@danieldai
Copy link

I meet this issue with V4.0.3, (No overload matchs this call) with different promise types and more than 10 promises

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants