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 const
when passing the array toPromise.all
.