Skip to content

Commit 787da6f

Browse files
committed
Add special inference rule for 'T | PromiseLike<T>'
1 parent e25abe3 commit 787da6f

File tree

11 files changed

+1490
-332
lines changed

11 files changed

+1490
-332
lines changed

src/compiler/checker.ts

Lines changed: 47 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25992,6 +25992,38 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2599225992
return isTupleType(type) && getTupleElementType(type, 0) === getIndexedAccessType(typeParameter, getNumberLiteralType(0)) && !getTypeOfPropertyOfType(type, "1" as __String);
2599325993
}
2599425994

25995+
function isAwaitedLikeType(type: Type) {
25996+
if (type.flags & TypeFlags.Union) {
25997+
let typeVariable: Type | undefined;
25998+
let promisedType: Type | undefined;
25999+
for (const t of (type as UnionType).types) {
26000+
if (t.flags & TypeFlags.TypeVariable) {
26001+
if (typeVariable) {
26002+
return false;
26003+
}
26004+
typeVariable = t;
26005+
if (promisedType) {
26006+
return typeVariable === promisedType;
26007+
}
26008+
continue;
26009+
}
26010+
if (isPromiseType(t)) {
26011+
if (promisedType) {
26012+
return false;
26013+
}
26014+
[promisedType] = getTypeArguments(t as TypeReference);
26015+
if (typeVariable) {
26016+
return typeVariable === promisedType;
26017+
}
26018+
continue;
26019+
}
26020+
return false;
26021+
}
26022+
return true;
26023+
}
26024+
return false;
26025+
}
26026+
2599526027
function inferTypes(inferences: InferenceInfo[], originalSource: Type, originalTarget: Type, priority = InferencePriority.None, contravariant = false) {
2599626028
let bivariant = false;
2599726029
let propagationType: Type;
@@ -26016,6 +26048,11 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2601626048
propagationType = savePropagationType;
2601726049
return;
2601826050
}
26051+
// If the target is either `T | Promise<T>` or `T | PromiseLike<T>`, continue inferring types with the
26052+
// `AwaitedLikeType` priority.
26053+
if (!(priority & InferencePriority.AwaitedLikeType) && isAwaitedLikeType(target)) {
26054+
priority |= InferencePriority.AwaitedLikeType;
26055+
}
2601926056
if (source.aliasSymbol && source.aliasSymbol === target.aliasSymbol) {
2602026057
if (source.aliasTypeArguments) {
2602126058
// Source and target are types originating in the same generic type alias declaration.
@@ -26165,7 +26202,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2616526202
}
2616626203
if (
2616726204
getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
26168-
(source as TypeReference).target === (target as TypeReference).target || isArrayType(source) && isArrayType(target)
26205+
(source as TypeReference).target === (target as TypeReference).target ||
26206+
isArrayType(source) && isArrayType(target) ||
26207+
isPromiseType(source) && isPromiseType(target)
2616926208
) &&
2617026209
!((source as TypeReference).node && (target as TypeReference).node)
2617126210
) {
@@ -26583,7 +26622,9 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
2658326622
function inferFromObjectTypes(source: Type, target: Type) {
2658426623
if (
2658526624
getObjectFlags(source) & ObjectFlags.Reference && getObjectFlags(target) & ObjectFlags.Reference && (
26586-
(source as TypeReference).target === (target as TypeReference).target || isArrayType(source) && isArrayType(target)
26625+
(source as TypeReference).target === (target as TypeReference).target ||
26626+
isArrayType(source) && isArrayType(target) ||
26627+
isPromiseType(source) && isPromiseType(target)
2658726628
)
2658826629
) {
2658926630
// If source and target are references to the same generic type, infer from type arguments
@@ -42488,6 +42529,10 @@ export function createTypeChecker(host: TypeCheckerHost): TypeChecker {
4248842529
}
4248942530
}
4249042531

42532+
function isPromiseType(type: Type) {
42533+
return isReferenceToSomeType(type, [getGlobalPromiseType(/*reportErrors*/ false), getGlobalPromiseLikeType(/*reportErrors*/ false)]);
42534+
}
42535+
4249142536
function getAwaitedTypeOfPromise(type: Type, errorNode?: Node, diagnosticMessage?: DiagnosticMessage, ...args: DiagnosticArguments): Type | undefined {
4249242537
const promisedType = getPromisedTypeOfPromise(type, errorNode);
4249342538
return promisedType && getAwaitedType(promisedType, errorNode, diagnosticMessage, ...args);

src/compiler/types.ts

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7027,19 +7027,20 @@ export type TypeMapper =
70277027
export const enum InferencePriority {
70287028
None = 0,
70297029
NakedTypeVariable = 1 << 0, // Naked type variable in union or intersection type
7030-
SpeculativeTuple = 1 << 1, // Speculative tuple inference
7031-
SubstituteSource = 1 << 2, // Source of inference originated within a substitution type's substitute
7032-
HomomorphicMappedType = 1 << 3, // Reverse inference for homomorphic mapped type
7033-
PartialHomomorphicMappedType = 1 << 4, // Partial reverse inference for homomorphic mapped type
7034-
MappedTypeConstraint = 1 << 5, // Reverse inference for mapped type
7035-
ContravariantConditional = 1 << 6, // Conditional type in contravariant position
7036-
ReturnType = 1 << 7, // Inference made from return type of generic function
7037-
LiteralKeyof = 1 << 8, // Inference made from a string literal to a keyof T
7038-
NoConstraints = 1 << 9, // Don't infer from constraints of instantiable types
7039-
AlwaysStrict = 1 << 10, // Always use strict rules for contravariant inferences
7040-
MaxValue = 1 << 11, // Seed for inference priority tracking
7041-
7042-
PriorityImpliesCombination = ReturnType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
7030+
AwaitedLikeType = 1 << 1, // A type that is essentially awaited
7031+
SpeculativeTuple = 1 << 2, // Speculative tuple inference
7032+
SubstituteSource = 1 << 3, // Source of inference originated within a substitution type's substitute
7033+
HomomorphicMappedType = 1 << 4, // Reverse inference for homomorphic mapped type
7034+
PartialHomomorphicMappedType = 1 << 5, // Partial reverse inference for homomorphic mapped type
7035+
MappedTypeConstraint = 1 << 6, // Reverse inference for mapped type
7036+
ContravariantConditional = 1 << 7, // Conditional type in contravariant position
7037+
ReturnType = 1 << 8, // Inference made from return type of generic function
7038+
LiteralKeyof = 1 << 9, // Inference made from a string literal to a keyof T
7039+
NoConstraints = 1 << 10, // Don't infer from constraints of instantiable types
7040+
AlwaysStrict = 1 << 11, // Always use strict rules for contravariant inferences
7041+
MaxValue = 1 << 12, // Seed for inference priority tracking
7042+
7043+
PriorityImpliesCombination = ReturnType | AwaitedLikeType | MappedTypeConstraint | LiteralKeyof, // These priorities imply that the resulting type should be a combination of all candidates
70437044
Circularity = -1, // Inference circularity (value less than all other priorities)
70447045
}
70457046

tests/baselines/reference/api/typescript.d.ts

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -6861,18 +6861,19 @@ declare namespace ts {
68616861
enum InferencePriority {
68626862
None = 0,
68636863
NakedTypeVariable = 1,
6864-
SpeculativeTuple = 2,
6865-
SubstituteSource = 4,
6866-
HomomorphicMappedType = 8,
6867-
PartialHomomorphicMappedType = 16,
6868-
MappedTypeConstraint = 32,
6869-
ContravariantConditional = 64,
6870-
ReturnType = 128,
6871-
LiteralKeyof = 256,
6872-
NoConstraints = 512,
6873-
AlwaysStrict = 1024,
6874-
MaxValue = 2048,
6875-
PriorityImpliesCombination = 416,
6864+
AwaitedLikeType = 2,
6865+
SpeculativeTuple = 4,
6866+
SubstituteSource = 8,
6867+
HomomorphicMappedType = 16,
6868+
PartialHomomorphicMappedType = 32,
6869+
MappedTypeConstraint = 64,
6870+
ContravariantConditional = 128,
6871+
ReturnType = 256,
6872+
LiteralKeyof = 512,
6873+
NoConstraints = 1024,
6874+
AlwaysStrict = 2048,
6875+
MaxValue = 4096,
6876+
PriorityImpliesCombination = 834,
68766877
Circularity = -1,
68776878
}
68786879
interface FileExtensionInfo {

tests/baselines/reference/promiseType.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
//// [promiseType.ts]
44
declare var p: Promise<boolean>;
55
declare var x: any;
6+
declare var b: bigint;
67

78
async function A() {
89
const a = await p;
@@ -98,6 +99,7 @@ const p16 = p.catch(() => {});
9899
const p17 = p.catch(() => {throw 1});
99100
const p18 = p.catch(() => Promise.reject(1));
100101
const p19 = p.catch(() => Promise.resolve(1));
102+
const p19a = p.catch(() => b && Promise.resolve(1));
101103

102104
const p20 = p.then(undefined);
103105
const p21 = p.then(null);
@@ -108,6 +110,7 @@ const p25 = p.then(() => null);
108110
const p26 = p.then(() => {});
109111
const p27 = p.then(() => {throw 1});
110112
const p28 = p.then(() => Promise.resolve(1));
113+
const p28a = p.then(() => b && Promise.resolve(1));
111114
const p29 = p.then(() => Promise.reject(1));
112115

113116
const p30 = p.then(undefined, undefined);
@@ -119,6 +122,7 @@ const p35 = p.then(undefined, () => null);
119122
const p36 = p.then(undefined, () => {});
120123
const p37 = p.then(undefined, () => {throw 1});
121124
const p38 = p.then(undefined, () => Promise.resolve(1));
125+
const p38a = p.then(undefined, () => b && Promise.resolve(1));
122126
const p39 = p.then(undefined, () => Promise.reject(1));
123127

124128
const p40 = p.then(null, undefined);
@@ -130,6 +134,7 @@ const p45 = p.then(null, () => null);
130134
const p46 = p.then(null, () => {});
131135
const p47 = p.then(null, () => {throw 1});
132136
const p48 = p.then(null, () => Promise.resolve(1));
137+
const p48a = p.then(null, () => b && Promise.resolve(1));
133138
const p49 = p.then(null, () => Promise.reject(1));
134139

135140
const p50 = p.then(() => "1", undefined);
@@ -141,6 +146,7 @@ const p55 = p.then(() => "1", () => null);
141146
const p56 = p.then(() => "1", () => {});
142147
const p57 = p.then(() => "1", () => {throw 1});
143148
const p58 = p.then(() => "1", () => Promise.resolve(1));
149+
const p58a = p.then(() => "1", () => b && Promise.resolve(1));
144150
const p59 = p.then(() => "1", () => Promise.reject(1));
145151

146152
const p60 = p.then(() => x, undefined);
@@ -152,6 +158,7 @@ const p65 = p.then(() => x, () => null);
152158
const p66 = p.then(() => x, () => {});
153159
const p67 = p.then(() => x, () => {throw 1});
154160
const p68 = p.then(() => x, () => Promise.resolve(1));
161+
const p68a = p.then(() => x, () => b && Promise.resolve(1));
155162
const p69 = p.then(() => x, () => Promise.reject(1));
156163

157164
const p70 = p.then(() => undefined, undefined);
@@ -163,6 +170,7 @@ const p75 = p.then(() => undefined, () => null);
163170
const p76 = p.then(() => undefined, () => {});
164171
const p77 = p.then(() => undefined, () => {throw 1});
165172
const p78 = p.then(() => undefined, () => Promise.resolve(1));
173+
const p78a = p.then(() => undefined, () => b && Promise.resolve(1));
166174
const p79 = p.then(() => undefined, () => Promise.reject(1));
167175

168176
const p80 = p.then(() => null, undefined);
@@ -174,6 +182,7 @@ const p85 = p.then(() => null, () => null);
174182
const p86 = p.then(() => null, () => {});
175183
const p87 = p.then(() => null, () => {throw 1});
176184
const p88 = p.then(() => null, () => Promise.resolve(1));
185+
const p88a = p.then(() => null, () => b && Promise.resolve(1));
177186
const p89 = p.then(() => null, () => Promise.reject(1));
178187

179188
const p90 = p.then(() => {}, undefined);
@@ -185,6 +194,7 @@ const p95 = p.then(() => {}, () => null);
185194
const p96 = p.then(() => {}, () => {});
186195
const p97 = p.then(() => {}, () => {throw 1});
187196
const p98 = p.then(() => {}, () => Promise.resolve(1));
197+
const p98a = p.then(() => {}, () => b && Promise.resolve(1));
188198
const p99 = p.then(() => {}, () => Promise.reject(1));
189199

190200
const pa0 = p.then(() => {throw 1}, undefined);
@@ -196,6 +206,7 @@ const pa5 = p.then(() => {throw 1}, () => null);
196206
const pa6 = p.then(() => {throw 1}, () => {});
197207
const pa7 = p.then(() => {throw 1}, () => {throw 1});
198208
const pa8 = p.then(() => {throw 1}, () => Promise.resolve(1));
209+
const pa8a = p.then(() => {throw 1}, () => b && Promise.resolve(1));
199210
const pa9 = p.then(() => {throw 1}, () => Promise.reject(1));
200211

201212
const pb0 = p.then(() => Promise.resolve("1"), undefined);
@@ -207,6 +218,7 @@ const pb5 = p.then(() => Promise.resolve("1"), () => null);
207218
const pb6 = p.then(() => Promise.resolve("1"), () => {});
208219
const pb7 = p.then(() => Promise.resolve("1"), () => {throw 1});
209220
const pb8 = p.then(() => Promise.resolve("1"), () => Promise.resolve(1));
221+
const pb8a = p.then(() => Promise.resolve("1"), () => b && Promise.resolve(1));
210222
const pb9 = p.then(() => Promise.resolve("1"), () => Promise.reject(1));
211223

212224
const pc0 = p.then(() => Promise.reject("1"), undefined);
@@ -218,13 +230,15 @@ const pc5 = p.then(() => Promise.reject("1"), () => null);
218230
const pc6 = p.then(() => Promise.reject("1"), () => {});
219231
const pc7 = p.then(() => Promise.reject("1"), () => {throw 1});
220232
const pc8 = p.then(() => Promise.reject("1"), () => Promise.resolve(1));
233+
const pc8a = p.then(() => Promise.reject("1"), () => b && Promise.resolve(1));
221234
const pc9 = p.then(() => Promise.reject("1"), () => Promise.reject(1));
222235

223236
Promise.resolve(undefined as Promise<string> | number);
224237
Promise.resolve(undefined as Promise<Promise<number>>);
225238
Promise.resolve(undefined as string | Promise<Promise<number>>);
226239
Promise.resolve(undefined as Promise<string> | Promise<Promise<number>>);
227-
Promise.resolve(undefined as Promise<string | Promise<Promise<number>>>);
240+
Promise.resolve(undefined as Promise<string | Promise<Promise<number>>>);
241+
228242

229243
//// [promiseType.js]
230244
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
@@ -337,6 +351,7 @@ const p16 = p.catch(() => { });
337351
const p17 = p.catch(() => { throw 1; });
338352
const p18 = p.catch(() => Promise.reject(1));
339353
const p19 = p.catch(() => Promise.resolve(1));
354+
const p19a = p.catch(() => b && Promise.resolve(1));
340355
const p20 = p.then(undefined);
341356
const p21 = p.then(null);
342357
const p22 = p.then(() => 1);
@@ -346,6 +361,7 @@ const p25 = p.then(() => null);
346361
const p26 = p.then(() => { });
347362
const p27 = p.then(() => { throw 1; });
348363
const p28 = p.then(() => Promise.resolve(1));
364+
const p28a = p.then(() => b && Promise.resolve(1));
349365
const p29 = p.then(() => Promise.reject(1));
350366
const p30 = p.then(undefined, undefined);
351367
const p31 = p.then(undefined, null);
@@ -356,6 +372,7 @@ const p35 = p.then(undefined, () => null);
356372
const p36 = p.then(undefined, () => { });
357373
const p37 = p.then(undefined, () => { throw 1; });
358374
const p38 = p.then(undefined, () => Promise.resolve(1));
375+
const p38a = p.then(undefined, () => b && Promise.resolve(1));
359376
const p39 = p.then(undefined, () => Promise.reject(1));
360377
const p40 = p.then(null, undefined);
361378
const p41 = p.then(null, null);
@@ -366,6 +383,7 @@ const p45 = p.then(null, () => null);
366383
const p46 = p.then(null, () => { });
367384
const p47 = p.then(null, () => { throw 1; });
368385
const p48 = p.then(null, () => Promise.resolve(1));
386+
const p48a = p.then(null, () => b && Promise.resolve(1));
369387
const p49 = p.then(null, () => Promise.reject(1));
370388
const p50 = p.then(() => "1", undefined);
371389
const p51 = p.then(() => "1", null);
@@ -376,6 +394,7 @@ const p55 = p.then(() => "1", () => null);
376394
const p56 = p.then(() => "1", () => { });
377395
const p57 = p.then(() => "1", () => { throw 1; });
378396
const p58 = p.then(() => "1", () => Promise.resolve(1));
397+
const p58a = p.then(() => "1", () => b && Promise.resolve(1));
379398
const p59 = p.then(() => "1", () => Promise.reject(1));
380399
const p60 = p.then(() => x, undefined);
381400
const p61 = p.then(() => x, null);
@@ -386,6 +405,7 @@ const p65 = p.then(() => x, () => null);
386405
const p66 = p.then(() => x, () => { });
387406
const p67 = p.then(() => x, () => { throw 1; });
388407
const p68 = p.then(() => x, () => Promise.resolve(1));
408+
const p68a = p.then(() => x, () => b && Promise.resolve(1));
389409
const p69 = p.then(() => x, () => Promise.reject(1));
390410
const p70 = p.then(() => undefined, undefined);
391411
const p71 = p.then(() => undefined, null);
@@ -396,6 +416,7 @@ const p75 = p.then(() => undefined, () => null);
396416
const p76 = p.then(() => undefined, () => { });
397417
const p77 = p.then(() => undefined, () => { throw 1; });
398418
const p78 = p.then(() => undefined, () => Promise.resolve(1));
419+
const p78a = p.then(() => undefined, () => b && Promise.resolve(1));
399420
const p79 = p.then(() => undefined, () => Promise.reject(1));
400421
const p80 = p.then(() => null, undefined);
401422
const p81 = p.then(() => null, null);
@@ -406,6 +427,7 @@ const p85 = p.then(() => null, () => null);
406427
const p86 = p.then(() => null, () => { });
407428
const p87 = p.then(() => null, () => { throw 1; });
408429
const p88 = p.then(() => null, () => Promise.resolve(1));
430+
const p88a = p.then(() => null, () => b && Promise.resolve(1));
409431
const p89 = p.then(() => null, () => Promise.reject(1));
410432
const p90 = p.then(() => { }, undefined);
411433
const p91 = p.then(() => { }, null);
@@ -416,6 +438,7 @@ const p95 = p.then(() => { }, () => null);
416438
const p96 = p.then(() => { }, () => { });
417439
const p97 = p.then(() => { }, () => { throw 1; });
418440
const p98 = p.then(() => { }, () => Promise.resolve(1));
441+
const p98a = p.then(() => { }, () => b && Promise.resolve(1));
419442
const p99 = p.then(() => { }, () => Promise.reject(1));
420443
const pa0 = p.then(() => { throw 1; }, undefined);
421444
const pa1 = p.then(() => { throw 1; }, null);
@@ -426,6 +449,7 @@ const pa5 = p.then(() => { throw 1; }, () => null);
426449
const pa6 = p.then(() => { throw 1; }, () => { });
427450
const pa7 = p.then(() => { throw 1; }, () => { throw 1; });
428451
const pa8 = p.then(() => { throw 1; }, () => Promise.resolve(1));
452+
const pa8a = p.then(() => { throw 1; }, () => b && Promise.resolve(1));
429453
const pa9 = p.then(() => { throw 1; }, () => Promise.reject(1));
430454
const pb0 = p.then(() => Promise.resolve("1"), undefined);
431455
const pb1 = p.then(() => Promise.resolve("1"), null);
@@ -436,6 +460,7 @@ const pb5 = p.then(() => Promise.resolve("1"), () => null);
436460
const pb6 = p.then(() => Promise.resolve("1"), () => { });
437461
const pb7 = p.then(() => Promise.resolve("1"), () => { throw 1; });
438462
const pb8 = p.then(() => Promise.resolve("1"), () => Promise.resolve(1));
463+
const pb8a = p.then(() => Promise.resolve("1"), () => b && Promise.resolve(1));
439464
const pb9 = p.then(() => Promise.resolve("1"), () => Promise.reject(1));
440465
const pc0 = p.then(() => Promise.reject("1"), undefined);
441466
const pc1 = p.then(() => Promise.reject("1"), null);
@@ -446,6 +471,7 @@ const pc5 = p.then(() => Promise.reject("1"), () => null);
446471
const pc6 = p.then(() => Promise.reject("1"), () => { });
447472
const pc7 = p.then(() => Promise.reject("1"), () => { throw 1; });
448473
const pc8 = p.then(() => Promise.reject("1"), () => Promise.resolve(1));
474+
const pc8a = p.then(() => Promise.reject("1"), () => b && Promise.resolve(1));
449475
const pc9 = p.then(() => Promise.reject("1"), () => Promise.reject(1));
450476
Promise.resolve(undefined);
451477
Promise.resolve(undefined);

0 commit comments

Comments
 (0)