mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-02-09 02:30:15 -06:00
Add 'T | PromiseLike<T>' inference from awaited types
This commit is contained in:
parent
9119fe3797
commit
17517d5085
@ -18441,10 +18441,45 @@ namespace ts {
|
||||
return typeVariable;
|
||||
}
|
||||
|
||||
function isPromiseForType(promiseType: Type, promisedType: Type) {
|
||||
return getPromisedTypeOfPromise(promiseType) === promisedType;
|
||||
}
|
||||
|
||||
function inferToMultipleTypes(source: Type, targets: Type[], targetFlags: TypeFlags) {
|
||||
let typeVariableCount = 0;
|
||||
if (targetFlags & TypeFlags.Union) {
|
||||
let nakedTypeVariable: Type | undefined;
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
nakedTypeVariable = t;
|
||||
typeVariableCount++;
|
||||
}
|
||||
}
|
||||
|
||||
// To better handle inference for `Promise`-like types, we detect a target union of `T | PromiseLike<T>`
|
||||
// (for any compatible `PromiseLike`). When encountered, we infer from source to the type parameter `T`,
|
||||
// where each type of source is mapped to extract the promised type of any promise (e.g.,
|
||||
// `string | Promise<number>` becomes `string | number`).
|
||||
if (typeVariableCount === 1) {
|
||||
let remainderArePromises = false;
|
||||
for (const t of targets) {
|
||||
if (!getInferenceInfoForType(t)) {
|
||||
if (isPromiseForType(t, nakedTypeVariable!)) {
|
||||
remainderArePromises = true;
|
||||
}
|
||||
else {
|
||||
remainderArePromises = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// remaining constituents are promise-like types whose "promised types" are `T`
|
||||
if (remainderArePromises) {
|
||||
inferFromTypes(mapType(source, s => getPromisedTypeOfPromise(s) ?? s), nakedTypeVariable!);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const sources = source.flags & TypeFlags.Union ? (<UnionType>source).types : [source];
|
||||
const matched = new Array<boolean>(sources.length);
|
||||
let inferenceCircularity = false;
|
||||
@ -18453,11 +18488,7 @@ namespace ts {
|
||||
// equal priority (i.e. of equal quality) to what we would infer for a naked type
|
||||
// parameter.
|
||||
for (const t of targets) {
|
||||
if (getInferenceInfoForType(t)) {
|
||||
nakedTypeVariable = t;
|
||||
typeVariableCount++;
|
||||
}
|
||||
else {
|
||||
if (!getInferenceInfoForType(t)) {
|
||||
for (let i = 0; i < sources.length; i++) {
|
||||
const saveInferencePriority = inferencePriority;
|
||||
inferencePriority = InferencePriority.MaxValue;
|
||||
@ -30128,11 +30159,12 @@ namespace ts {
|
||||
return typeAsPromise.promisedTypeOfPromise;
|
||||
}
|
||||
|
||||
if (isReferenceToType(type, getGlobalPromiseType(/*reportErrors*/ false))) {
|
||||
if (isReferenceToType(type, getGlobalPromiseType(/*reportErrors*/ false)) ||
|
||||
isReferenceToType(type, getGlobalPromiseLikeType(/*reportErrors*/ false))) {
|
||||
return typeAsPromise.promisedTypeOfPromise = getTypeArguments(<GenericType>type)[0];
|
||||
}
|
||||
|
||||
const thenFunction = getTypeOfPropertyOfType(type, "then" as __String)!; // TODO: GH#18217
|
||||
const thenFunction = getTypeOfPropertyOfType(type, "then" as __String);
|
||||
if (isTypeAny(thenFunction)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
67
tests/baselines/reference/promiseTypeInference2.symbols
Normal file
67
tests/baselines/reference/promiseTypeInference2.symbols
Normal file
@ -0,0 +1,67 @@
|
||||
=== tests/cases/compiler/promiseTypeInference2.ts ===
|
||||
const p1 = Promise.resolve(null);
|
||||
>p1 : Symbol(p1, Decl(promiseTypeInference2.ts, 0, 5))
|
||||
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
|
||||
const p2 = p1.then(() => 100);
|
||||
>p2 : Symbol(p2, Decl(promiseTypeInference2.ts, 1, 5))
|
||||
>p1.then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>p1 : Symbol(p1, Decl(promiseTypeInference2.ts, 0, 5))
|
||||
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
|
||||
const p3 = p1.then(() => Promise.resolve(100));
|
||||
>p3 : Symbol(p3, Decl(promiseTypeInference2.ts, 2, 5))
|
||||
>p1.then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>p1 : Symbol(p1, Decl(promiseTypeInference2.ts, 0, 5))
|
||||
>then : Symbol(Promise.then, Decl(lib.es5.d.ts, --, --))
|
||||
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
|
||||
declare const p4: Promise<number> | Promise<string>;
|
||||
>p4 : Symbol(p4, Decl(promiseTypeInference2.ts, 4, 13))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
|
||||
const p5 = Promise.resolve(p4);
|
||||
>p5 : Symbol(p5, Decl(promiseTypeInference2.ts, 5, 5))
|
||||
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
>p4 : Symbol(p4, Decl(promiseTypeInference2.ts, 4, 13))
|
||||
|
||||
declare const p6: PromiseLike<number> & { x: 1 } | PromiseLike<string> & { x: 2 };
|
||||
>p6 : Symbol(p6, Decl(promiseTypeInference2.ts, 7, 13))
|
||||
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(promiseTypeInference2.ts, 7, 41))
|
||||
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
|
||||
>x : Symbol(x, Decl(promiseTypeInference2.ts, 7, 74))
|
||||
|
||||
const p7 = Promise.resolve(p6);
|
||||
>p7 : Symbol(p7, Decl(promiseTypeInference2.ts, 8, 5))
|
||||
>Promise.resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
>resolve : Symbol(PromiseConstructor.resolve, Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --))
|
||||
>p6 : Symbol(p6, Decl(promiseTypeInference2.ts, 7, 13))
|
||||
|
||||
declare function resolve<T>(value: T | PromiseLike<T> & { x: 1 } | PromiseLike<T> & { x: 2 }): Promise<T>;
|
||||
>resolve : Symbol(resolve, Decl(promiseTypeInference2.ts, 8, 31))
|
||||
>T : Symbol(T, Decl(promiseTypeInference2.ts, 10, 25))
|
||||
>value : Symbol(value, Decl(promiseTypeInference2.ts, 10, 28))
|
||||
>T : Symbol(T, Decl(promiseTypeInference2.ts, 10, 25))
|
||||
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(promiseTypeInference2.ts, 10, 25))
|
||||
>x : Symbol(x, Decl(promiseTypeInference2.ts, 10, 57))
|
||||
>PromiseLike : Symbol(PromiseLike, Decl(lib.es5.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(promiseTypeInference2.ts, 10, 25))
|
||||
>x : Symbol(x, Decl(promiseTypeInference2.ts, 10, 85))
|
||||
>Promise : Symbol(Promise, Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.promise.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --))
|
||||
>T : Symbol(T, Decl(promiseTypeInference2.ts, 10, 25))
|
||||
|
||||
const p8 = resolve(p6);
|
||||
>p8 : Symbol(p8, Decl(promiseTypeInference2.ts, 11, 5))
|
||||
>resolve : Symbol(resolve, Decl(promiseTypeInference2.ts, 8, 31))
|
||||
>p6 : Symbol(p6, Decl(promiseTypeInference2.ts, 7, 13))
|
||||
|
||||
67
tests/baselines/reference/promiseTypeInference2.types
Normal file
67
tests/baselines/reference/promiseTypeInference2.types
Normal file
@ -0,0 +1,67 @@
|
||||
=== tests/cases/compiler/promiseTypeInference2.ts ===
|
||||
const p1 = Promise.resolve(null);
|
||||
>p1 : Promise<any>
|
||||
>Promise.resolve(null) : Promise<any>
|
||||
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>Promise : PromiseConstructor
|
||||
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>null : null
|
||||
|
||||
const p2 = p1.then(() => 100);
|
||||
>p2 : Promise<number>
|
||||
>p1.then(() => 100) : Promise<number>
|
||||
>p1.then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>p1 : Promise<any>
|
||||
>then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>() => 100 : () => number
|
||||
>100 : 100
|
||||
|
||||
const p3 = p1.then(() => Promise.resolve(100));
|
||||
>p3 : Promise<number>
|
||||
>p1.then(() => Promise.resolve(100)) : Promise<number>
|
||||
>p1.then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>p1 : Promise<any>
|
||||
>then : <TResult1 = any, TResult2 = never>(onfulfilled?: (value: any) => TResult1 | PromiseLike<TResult1>, onrejected?: (reason: any) => TResult2 | PromiseLike<TResult2>) => Promise<TResult1 | TResult2>
|
||||
>() => Promise.resolve(100) : () => Promise<number>
|
||||
>Promise.resolve(100) : Promise<number>
|
||||
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>Promise : PromiseConstructor
|
||||
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>100 : 100
|
||||
|
||||
declare const p4: Promise<number> | Promise<string>;
|
||||
>p4 : Promise<number> | Promise<string>
|
||||
|
||||
const p5 = Promise.resolve(p4);
|
||||
>p5 : Promise<string | number>
|
||||
>Promise.resolve(p4) : Promise<string | number>
|
||||
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>Promise : PromiseConstructor
|
||||
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>p4 : Promise<number> | Promise<string>
|
||||
|
||||
declare const p6: PromiseLike<number> & { x: 1 } | PromiseLike<string> & { x: 2 };
|
||||
>p6 : (PromiseLike<number> & { x: 1; }) | (PromiseLike<string> & { x: 2; })
|
||||
>x : 1
|
||||
>x : 2
|
||||
|
||||
const p7 = Promise.resolve(p6);
|
||||
>p7 : Promise<string | number>
|
||||
>Promise.resolve(p6) : Promise<string | number>
|
||||
>Promise.resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>Promise : PromiseConstructor
|
||||
>resolve : { <T>(value: T | PromiseLike<T>): Promise<T>; (): Promise<void>; }
|
||||
>p6 : (PromiseLike<number> & { x: 1; }) | (PromiseLike<string> & { x: 2; })
|
||||
|
||||
declare function resolve<T>(value: T | PromiseLike<T> & { x: 1 } | PromiseLike<T> & { x: 2 }): Promise<T>;
|
||||
>resolve : <T>(value: T | (PromiseLike<T> & { x: 1; }) | (PromiseLike<T> & { x: 2; })) => Promise<T>
|
||||
>value : T | (PromiseLike<T> & { x: 1; }) | (PromiseLike<T> & { x: 2; })
|
||||
>x : 1
|
||||
>x : 2
|
||||
|
||||
const p8 = resolve(p6);
|
||||
>p8 : Promise<string | number>
|
||||
>resolve(p6) : Promise<string | number>
|
||||
>resolve : <T>(value: T | (PromiseLike<T> & { x: 1; }) | (PromiseLike<T> & { x: 2; })) => Promise<T>
|
||||
>p6 : (PromiseLike<number> & { x: 1; }) | (PromiseLike<string> & { x: 2; })
|
||||
|
||||
15
tests/cases/compiler/promiseTypeInference2.ts
Normal file
15
tests/cases/compiler/promiseTypeInference2.ts
Normal file
@ -0,0 +1,15 @@
|
||||
// @target: es2015
|
||||
// @noEmit: true
|
||||
|
||||
const p1 = Promise.resolve(null);
|
||||
const p2 = p1.then(() => 100);
|
||||
const p3 = p1.then(() => Promise.resolve(100));
|
||||
|
||||
declare const p4: Promise<number> | Promise<string>;
|
||||
const p5 = Promise.resolve(p4);
|
||||
|
||||
declare const p6: PromiseLike<number> & { x: 1 } | PromiseLike<string> & { x: 2 };
|
||||
const p7 = Promise.resolve(p6);
|
||||
|
||||
declare function resolve<T>(value: T | PromiseLike<T> & { x: 1 } | PromiseLike<T> & { x: 2 }): Promise<T>;
|
||||
const p8 = resolve(p6);
|
||||
Loading…
x
Reference in New Issue
Block a user