@@ -18201,15 +18201,24 @@ namespace ts {
1820118201 return false;
1820218202 }
1820318203
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 {
1820518207 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.
1820818217 return type.symbol;
1820918218 }
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;
1821318222 }
1821418223 }
1821518224 if (type.flags & TypeFlags.IndexedAccess) {
@@ -19283,7 +19292,9 @@ namespace ts {
1928319292 let inferencePriority = InferencePriority.MaxValue;
1928419293 let allowComplexConstraintInference = true;
1928519294 let visited: ESMap<string, number>;
19286- let targetStack: Type[];
19295+ let sourceStack: object[];
19296+ let targetStack: object[];
19297+ let expandingFlags = ExpandingFlags.None;
1928719298 inferFromTypes(originalSource, originalTarget);
1928819299
1928919300 function inferFromTypes(source: Type, target: Type): void {
@@ -19406,7 +19417,7 @@ namespace ts {
1940619417 // Infer to the simplified version of an indexed access, if possible, to (hopefully) expose more bare type parameters to the inference engine
1940719418 const simplified = getSimplifiedType(target, /*writing*/ false);
1940819419 if (simplified !== target) {
19409- invokeWithDepthLimit (source, simplified, inferFromTypes);
19420+ invokeOnce (source, simplified, inferFromTypes);
1941019421 }
1941119422 else if (target.flags & TypeFlags.IndexedAccess) {
1941219423 const indexType = getSimplifiedType((target as IndexedAccessType).indexType, /*writing*/ false);
@@ -19415,7 +19426,7 @@ namespace ts {
1941519426 if (indexType.flags & TypeFlags.Instantiable) {
1941619427 const simplified = distributeIndexOverObjectType(getSimplifiedType((target as IndexedAccessType).objectType, /*writing*/ false), indexType, /*writing*/ false);
1941719428 if (simplified && simplified !== target) {
19418- invokeWithDepthLimit (source, simplified, inferFromTypes);
19429+ invokeOnce (source, simplified, inferFromTypes);
1941919430 }
1942019431 }
1942119432 }
@@ -19443,7 +19454,7 @@ namespace ts {
1944319454 inferFromTypes((<IndexedAccessType>source).indexType, (<IndexedAccessType>target).indexType);
1944419455 }
1944519456 else if (target.flags & TypeFlags.Conditional) {
19446- invokeWithDepthLimit(<ConditionalType> source, <ConditionalType> target, inferToConditionalType);
19457+ invokeOnce( source, target, inferToConditionalType);
1944719458 }
1944819459 else if (target.flags & TypeFlags.UnionOrIntersection) {
1944919460 inferToMultipleTypes(source, (<UnionOrIntersectionType>target).types, target.flags);
@@ -19476,7 +19487,7 @@ namespace ts {
1947619487 source = apparentSource;
1947719488 }
1947819489 if (source.flags & (TypeFlags.Object | TypeFlags.Intersection)) {
19479- invokeWithDepthLimit (source, target, inferFromObjectTypes);
19490+ invokeOnce (source, target, inferFromObjectTypes);
1948019491 }
1948119492 }
1948219493 if (source.flags & TypeFlags.Simplifiable) {
@@ -19494,7 +19505,7 @@ namespace ts {
1949419505 priority = savePriority;
1949519506 }
1949619507
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) {
1949819509 const key = source.id + "," + target.id;
1949919510 const status = visited && visited.get(key);
1950019511 if (status !== undefined) {
@@ -19504,17 +19515,24 @@ namespace ts {
1950419515 (visited || (visited = new Map<string, number>())).set(key, InferencePriority.Circularity);
1950519516 const saveInferencePriority = inferencePriority;
1950619517 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);
1951219528 action(source, target);
19529+ if (targetIdentity) targetStack.pop();
19530+ if (sourceIdentity) sourceStack.pop();
1951319531 }
1951419532 else {
1951519533 inferencePriority = InferencePriority.Circularity;
1951619534 }
19517- targetStack.pop() ;
19535+ expandingFlags = saveExpandingFlags ;
1951819536 visited.set(key, inferencePriority);
1951919537 inferencePriority = Math.min(inferencePriority, saveInferencePriority);
1952019538 }
@@ -19710,12 +19728,12 @@ namespace ts {
1971019728 return false;
1971119729 }
1971219730
19713- function inferToConditionalType(source: ConditionalType , target: ConditionalType) {
19731+ function inferToConditionalType(source: Type , target: ConditionalType) {
1971419732 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));
1971919737 }
1972019738 else {
1972119739 const savePriority = priority;
0 commit comments