Repository navigation
3.7.2 Promise.all infers base type of interfaces rather than actual interface #34937
Description
Activity
type Awaited<T> = T extends undefined ? T : T extends PromiseLike<infer U> ? U : T ; declare function all<T extends readonly any[]>( values: T ): Promise<{ -readonly [P in keyof T]: Awaited<T[P]> }>; //for empty tuple declare function betterAll (arr : []) : Promise<[]>; //for non-empty tuple declare function betterAll<ArrT extends readonly [any, ...any[]]>( arr: ArrT ): Promise<{ -readonly [i in keyof ArrT]: Awaited<ArrT[i]> }>; //for non-tuple array declare function betterAll<ArrT extends readonly any[]>( arr: ArrT ): Promise<{ -readonly [i in keyof ArrT]: Awaited<ArrT[i]> }>; //https:https://github.com/microsoft/TypeScript/issues/34937 interface QuoteFilter { _id: string; } interface QuoteFilterAdvanced extends QuoteFilter { name: string; } function getQuoteFilters(): Promise<QuoteFilter[]> { return new Promise<QuoteFilter[]>( (resolve, reject) => { resolve([ { _id: 'Test' } ]); }); } function getQuoteFiltersAdvanced(): Promise<QuoteFilterAdvanced[]> { return new Promise<QuoteFilterAdvanced[]>( (resolve, reject) => { resolve([ { _id: 'Test', name: 'Test' } ]); }); } async () => { //const filters: QuoteFilter[] //const advanced: QuoteFilter[] const [filters, advanced] = await Promise.all([ getQuoteFilters(), getQuoteFiltersAdvanced() ]); } async () => { //const filters: QuoteFilter[] //const advanced: QuoteFilterAdvanced[] const [filters, advanced] = await all([ getQuoteFilters(), getQuoteFiltersAdvanced() ]); } async () => { //const filters: QuoteFilter[] //const advanced: QuoteFilterAdvanced[] const [filters, advanced] = await betterAll([ getQuoteFilters(), getQuoteFiltersAdvanced() ]); }
See #33707
Reacted by Toni Villena, Kohei and Xiyang ChenSame here. This could look a bug
Reacted by XGHeavenThe same issue occurs even if you don't explicitly inherit types: playground, which looks very odd.
Try to renamefoo: string;to anything else (i.e.foz: string;) and the code compiles.Reacted by KoheiThis looks like a bug. I found
Promise.alluse wrong overload on the above example.
It use definition inlib.es2015.iterable.d.tsnotlib.es2015.promise.d.ts.
But in old version, for example 3.6.2, It use correct definition inlib.es2015.promise.d.tsaleksandar-kandzhichki commented
on Nov 7, 2019 More actionsXGHeaven I believe the incorrect results you are getting are indeed from the overload in
es2015.promise.d.ts(not the one ines2015.iterable.d.ts). In my tests, if I drop all but the overload fromes2015.promise.d.ts, I continue to get the same results. FYI.Jack Bates (@jablko)
I can give you an example. In 3.7.2, It usees2015.iterable.d.ts. This is wrong. Because The type offirstandsecondisnumber | string.

Same code in 3.6.4. It usees2015.promise.d.ts. This is correct. Thefirsttype isnumberand thesecondtype isstring.

So the correct results should be come fromes2015.promise.d.ts.seeing same issue here... undefined is added to everything I call in a Promise.all...
- changed the title
[-]3.7.2 Promise.all does infers base type of interfaces rather than actual interface[/-][+]3.7.2 Promise.all infers base type of interfaces rather than actual interface[/+]on Nov 10, 2019 XGHeaven I see what you mean, however I think you've highlighted a separate issue with the playground: That example doesn't work in the playground regardless of version (3.7.2 or 3.6.3). https://www.typescriptlang.org/play/index.html?ts=3.6.3#code/IYZwngdgxgBAZgV2gFwJYHsIwOYFNkCCANkQJLK4C2IAFAJQwDeAUDGzFJiMjANpyoATtwA0MELk4QAJgF0AXHwgJKAI1yCx3QagjZZMALwxgAd2CoeABUHpKqCQDpgJGrwCMYgORfZdANzMAL5AA
I think it's a symptom of microsoft/TypeScript-Website#82 (the playground doesn't use the lib d.ts files that get shipped with a build).
In my tests (locally) it doesn't use
es2015.iterable.d.ts, in 3.6 or 3.7. (I do get the same incorrect results as you in 3.7, but they're not coming fromes2015.iterable.d.ts.)Reacted by XGHeavenI found something! Is it possible that the added
readonlyin version 3.7.2 messes things up? I did an experiment…In my case, all types from all Promises are mashed together in TypeScript 3.7.2, not in 3.6.3. As far as I can tell by looking at
Promise.allin Visual Code Studio, both versions point tolib.es2015.promise.d.ts. In my example with three Promises, that's…In 3.7.2:
all<T1, T2, T3>(values: readonly [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;In 3.6.3:
all<T1, T2, T3>(values: [T1 | PromiseLike<T1>, T2 | PromiseLike<T2>, T3 | PromiseLike<T3>]): Promise<[T1, T2, T3]>;The only difference there is the added
readonlyin 3.7.2. So I commented out the above line from version 3.7.2 and replaced it with the line from 3.6.3 and the error went away!This was changed here: 29becf0#diff-ca94294a2cb9f29f16a30f5d32e56c2e
Reacted by Jack Bates and stronguiNick Singleton (@Manc) Yes, see #34501
A workaround for this is to use
as constwhen passing the array toPromise.all.Reacted by Robert Böhm, ottetz, Markus, Ivan Doroshenko, Tomáš Szabo, Tanapruk, Fran Hodl, Henning Hall, technatoria, wint and 3 moreReacted by Francesc Rosas, Hans-Peter Gygax, João Paulo Paschoal, aovchinn and Jason Candelora- addedBugA bug in TypeScriptA bug in TypeScriptFix AvailableA PR has been opened for this issueA PR has been opened for this issue
on Dec 2, 2019 Similar issue.
This works in
3.6.3and fails in3.7and3.8.0-dev.20191210async function foo(): Promise<number> { const [first, second] = await Promise.all([generateNumber(), generateStringOrNull()]); return first; // ERROR: Type 'number | null' is not assignable to type 'number'. } async function generateNumber(): Promise<number> { return 10; } async function generateStringOrNull(): Promise<string| null> { return '10'; }
firstshould benumbernot anumber | null.I can confirm that workaround works for me.
Reacted by aantthonyReacted by Nick SingletonWe've also hit with this when using 3.7.3. I can too confirm that the
as constworkaround works for me too.Reacted by Robert Böhm, Lukas Kühnert, Guilherme C. Matuella, aantthony, Renārs Vilnis, technatoria and vultix- locked as resolved and limited conversation to collaborators
on Oct 21, 2025

TypeScript 3.7.2
Playground link
Compiler Options:
{ "compilerOptions": { "noImplicitAny": true, "strictNullChecks": true, "strictFunctionTypes": true, "strictPropertyInitialization": true, "strictBindCallApply": true, "noImplicitThis": true, "noImplicitReturns": true, "useDefineForClassFields": false, "alwaysStrict": true, "allowUnreachableCode": false, "allowUnusedLabels": false, "downlevelIteration": false, "noEmitHelpers": false, "noLib": false, "noStrictGenericChecks": false, "noUnusedLocals": false, "noUnusedParameters": false, "esModuleInterop": true, "preserveConstEnums": false, "removeComments": false, "skipLibCheck": false, "checkJs": false, "allowJs": false, "experimentalDecorators": false, "emitDecoratorMetadata": false, "target": "ES2017", "module": "ESNext" } }Input:
Output:
Expected behavior:
I am expecting the advanced variable to be an array of QuoteFilterAdvanced rather than QuoteFilter.
This was correctly inferred in Typescript 3.6.3