@@ -20359,6 +20359,13 @@ namespace ts {
2035920359 // though highly unlikely, for this test to be true in a situation where a chain of instantiations is not infinitely
2036020360 // expanding. Effectively, we will generate a false positive when two types are structurally equal to at least maxDepth
2036120361 // levels, but unequal at some level beyond that.
20362+ // In addition, this will also detect when an indexed access has been chained off of maxDepth more times (which is
20363+ // essentially the dual of the structural comparison), and likewise mark the type as deeply nested, potentially adding
20364+ // false positives for finite but deeply expanding indexed accesses (eg, for `Q[P1][P2][P3][P4][P5]`).
20365+ // It also detects when a recursive type reference has expanded maxDepth or more times, e.g. if the true branch of
20366+ // `type A<T> = null extends T ? [A<NonNullable<T>>] : [T]`
20367+ // has expanded into `[A<NonNullable<NonNullable<NonNullable<NonNullable<NonNullable<T>>>>>>]`. In such cases we need
20368+ // to terminate the expansion, and we do so here.
2036220369 function isDeeplyNestedType(type: Type, stack: Type[], depth: number, maxDepth = 3): boolean {
2036320370 if (depth >= maxDepth) {
2036420371 const identity = getRecursionIdentity(type);
@@ -20410,6 +20417,13 @@ namespace ts {
2041020417 if (type.flags & TypeFlags.TypeParameter) {
2041120418 return type.symbol;
2041220419 }
20420+ if (type.flags & TypeFlags.IndexedAccess) {
20421+ // Identity is the leftmost object type in a chain of indexed accesses, eg, in A[P][Q] it is A
20422+ do {
20423+ type = (type as IndexedAccessType).objectType;
20424+ } while (type.flags & TypeFlags.IndexedAccess);
20425+ return type;
20426+ }
2041320427 if (type.flags & TypeFlags.Conditional) {
2041420428 // The root object represents the origin of the conditional type
2041520429 return (type as ConditionalType).root;
0 commit comments