@@ -18201,15 +18201,24 @@ namespace ts {
18201
18201
return false;
18202
18202
}
18203
18203
18204
- function getRecursionIdentity(type: Type) {
18204
+ // Types with constituents that could circularly reference the type have a recursion identity. The recursion
18205
+ // identity is some object that is common to instantiations of the type with the same origin.
18206
+ function getRecursionIdentity(type: Type): object | undefined {
18205
18207
if (type.flags & TypeFlags.Object && !isObjectOrArrayLiteralType(type)) {
18206
- if (type.symbol) {
18207
- // We track all object types that have an associated symbol (representing the origin of the type)
18208
+ if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node) {
18209
+ // Deferred type references are tracked through their associated AST node. This gives us finer
18210
+ // granularity than using their associated target because each manifest type reference has a
18211
+ // unique AST node.
18212
+ return (type as TypeReference).node;
18213
+ }
18214
+ if (type.symbol && !(getObjectFlags(type) & ObjectFlags.Anonymous && type.symbol.flags & SymbolFlags.Class)) {
18215
+ // We track all object types that have an associated symbol (representing the origin of the type), but
18216
+ // exclude the static side of classes from this check since it shares its symbol with the instance side.
18208
18217
return type.symbol;
18209
18218
}
18210
- if (getObjectFlags(type) && ObjectFlags.Reference && (type as TypeReference).node || isTupleType(type)) {
18211
- // Deferred type references and tuple types are tracked through their target type
18212
- return ( type as TypeReference) .target;
18219
+ if (isTupleType(type)) {
18220
+ // Tuple types are tracked through their target type
18221
+ return type.target;
18213
18222
}
18214
18223
}
18215
18224
if (type.flags & TypeFlags.IndexedAccess) {
@@ -19283,7 +19292,9 @@ namespace ts {
19283
19292
let inferencePriority = InferencePriority.MaxValue;
19284
19293
let allowComplexConstraintInference = true;
19285
19294
let visited: ESMap<string, number>;
19286
- let targetStack: Type[];
19295
+ let sourceStack: object[];
19296
+ let targetStack: object[];
19297
+ let expandingFlags = ExpandingFlags.None;
19287
19298
inferFromTypes(originalSource, originalTarget);
19288
19299
19289
19300
function inferFromTypes(source: Type, target: Type): void {
@@ -19406,7 +19417,7 @@ namespace ts {
19406
19417
// Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
19407
19418
const simplified = getSimplifiedType(target, /*writing*/ false);
19408
19419
if (simplified !== target) {
19409
- invokeWithDepthLimit (source, simplified, inferFromTypes);
19420
+ invokeOnce (source, simplified, inferFromTypes);
19410
19421
}
19411
19422
else if (target.flags & TypeFlags.IndexedAccess) {
19412
19423
const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
@@ -19415,7 +19426,7 @@ namespace ts {
19415
19426
if (indexType.flags & TypeFlags.Instantiable) {
19416
19427
const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
19417
19428
if (simplified && simplified !== target) {
19418
- invokeWithDepthLimit (source, simplified, inferFromTypes);
19429
+ invokeOnce (source, simplified, inferFromTypes);
19419
19430
}
19420
19431
}
19421
19432
}
@@ -19443,7 +19454,7 @@ namespace ts {
19443
19454
inferFromTypes((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType);
19444
19455
}
19445
19456
else if (target.flags & TypeFlags.Conditional) {
19446
- invokeWithDepthLimit(<ConditionalType> source, <ConditionalType> target, inferToConditionalType);
19457
+ invokeOnce( source, target, inferToConditionalType);
19447
19458
}
19448
19459
else if (target.flags & TypeFlags.UnionOrIntersection) {
19449
19460
inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
@@ -19476,7 +19487,7 @@ namespace ts {
19476
19487
source = apparentSource;
19477
19488
}
19478
19489
if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
19479
- invokeWithDepthLimit (source, target, inferFromObjectTypes);
19490
+ invokeOnce (source, target, inferFromObjectTypes);
19480
19491
}
19481
19492
}
19482
19493
if (source.flags & TypeFlags.Simplifiable) {
@@ -19494,7 +19505,7 @@ namespace ts {
19494
19505
priority = savePriority;
19495
19506
}
19496
19507
19497
- function invokeWithDepthLimit (source: Type, target: Type, action: (source: Type, target: Type) => void) {
19508
+ function invokeOnce (source: Type, target: Type, action: (source: Type, target: Type) => void) {
19498
19509
const key = source.id + "," + target.id;
19499
19510
const status = visited && visited.get(key);
19500
19511
if (status !== undefined) {
@@ -19504,17 +19515,24 @@ namespace ts {
19504
19515
(visited || (visited = new Map<string, number>())).set(key, InferencePriority.Circularity);
19505
19516
const saveInferencePriority = inferencePriority;
19506
19517
inferencePriority = InferencePriority.MaxValue;
19507
- // It is possible for recursion to originate in generative types that create infinitely deep instantiations,
19508
- // with unique identities, for example 'type RecArray<T> = T | Array<RecArray<T>>'. We explore up to five
19509
- // nested instantiations of such types using the same isDeeplyNestedType check as recursiveTypeRelatedTo.
19510
- (targetStack || (targetStack = [])).push(target);
19511
- if (!isDeeplyNestedType(target, targetStack, targetStack.length)) {
19518
+ // We stop inferring and report a circularity if we encounter duplicate recursion identities on both
19519
+ // the source side and the target side.
19520
+ const saveExpandingFlags = expandingFlags;
19521
+ const sourceIdentity = getRecursionIdentity(source);
19522
+ const targetIdentity = getRecursionIdentity(target);
19523
+ if (sourceIdentity && contains(sourceStack, sourceIdentity)) expandingFlags |= ExpandingFlags.Source;
19524
+ if (targetIdentity && contains(targetStack, targetIdentity)) expandingFlags |= ExpandingFlags.Target;
19525
+ if (expandingFlags !== ExpandingFlags.Both) {
19526
+ if (sourceIdentity) (sourceStack || (sourceStack = [])).push(sourceIdentity);
19527
+ if (targetIdentity) (targetStack || (targetStack = [])).push(targetIdentity);
19512
19528
action(source, target);
19529
+ if (targetIdentity) targetStack.pop();
19530
+ if (sourceIdentity) sourceStack.pop();
19513
19531
}
19514
19532
else {
19515
19533
inferencePriority = InferencePriority.Circularity;
19516
19534
}
19517
- targetStack.pop() ;
19535
+ expandingFlags = saveExpandingFlags ;
19518
19536
visited.set(key, inferencePriority);
19519
19537
inferencePriority = Math.min(inferencePriority, saveInferencePriority);
19520
19538
}
@@ -19710,12 +19728,12 @@ namespace ts {
19710
19728
return false;
19711
19729
}
19712
19730
19713
- function inferToConditionalType(source: ConditionalType , target: ConditionalType) {
19731
+ function inferToConditionalType(source: Type , target: ConditionalType) {
19714
19732
if (source.flags & TypeFlags.Conditional) {
19715
- inferFromTypes(source.checkType, target.checkType);
19716
- inferFromTypes(source.extendsType, target.extendsType);
19717
- inferFromTypes(getTrueTypeFromConditionalType(source), getTrueTypeFromConditionalType(target));
19718
- inferFromTypes(getFalseTypeFromConditionalType(source), getFalseTypeFromConditionalType(target));
19733
+ inferFromTypes((<ConditionalType> source) .checkType, target.checkType);
19734
+ inferFromTypes((<ConditionalType> source) .extendsType, target.extendsType);
19735
+ inferFromTypes(getTrueTypeFromConditionalType(<ConditionalType> source), getTrueTypeFromConditionalType(target));
19736
+ inferFromTypes(getFalseTypeFromConditionalType(<ConditionalType> source), getFalseTypeFromConditionalType(target));
19719
19737
}
19720
19738
else {
19721
19739
const savePriority = priority;
0 commit comments