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:
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, advanced] = await Promise.all([
getQuoteFilters(),
getQuoteFiltersAdvanced()
]);
}
Output:
"use strict";
function getQuoteFilters() {
return new Promise((resolve, reject) => {
resolve([
{ _id: 'Test' }
]);
});
}
function getQuoteFiltersAdvanced() {
return new Promise((resolve, reject) => {
resolve([
{ _id: 'Test', name: 'Test' }
]);
});
}
async () => {
const [filters, advanced] = await Promise.all([
getQuoteFilters(),
getQuoteFiltersAdvanced()
]);
};
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
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://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()
]);
}
Same here. This could look a bug
The same issue occurs even if you don't explicitly inherit types: playground, which looks very odd.
Try to rename foo: string; to anything else (i.e.foz: string;) and the code compiles.
This looks like a bug. I found Promise.all use wrong overload on the above example.
It use definition in lib.es2015.iterable.d.ts not lib.es2015.promise.d.ts.
But in old version, for example 3.6.2, It use correct definition in lib.es2015.promise.d.ts
Same here, it does not infer the type properly

@XGHeaven I believe the incorrect results you are getting are indeed from the overload in es2015.promise.d.ts (not the one in es2015.iterable.d.ts). In my tests, if I drop all but the overload from es2015.promise.d.ts, I continue to get the same results. FYI.
@jablko
I can give you an example. In 3.7.2, It use es2015.iterable.d.ts. This is wrong. Because The type of first and second is number | string.

Same code in 3.6.4. It use es2015.promise.d.ts. This is correct. The first type is number and the second type is string.

So the correct results should be come from es2015.promise.d.ts.
seeing same issue here... undefined is added to everything I call in a Promise.all...
@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 from es2015.iterable.d.ts.)
I found something! Is it possible that the added readonly in 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.all in Visual Code Studio, both versions point to lib.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 readonly in 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: https://github.com/microsoft/TypeScript/commit/29becf05012bfa7ba20d50b0d16813971e46b8a6#diff-ca94294a2cb9f29f16a30f5d32e56c2e
@Manc Yes, see #34501
A workaround for this is to use as const when passing the array to Promise.all.
Similar issue.
This works in 3.6.3 and fails in 3.7 and 3.8.0-dev.20191210
async 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';
}
first should be number not a number | null.
I can confirm that workaround works for me.
We've also hit with this when using 3.7.3. I can too confirm that the as const workaround works for me too.
Most helpful comment
A workaround for this is to use
as constwhen passing the array toPromise.all.