From 2d6a750ddbf4aaf281f6ddc200dcb8b5d46dc63e Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Oct 2026 08:27:25 -0700 Subject: [PATCH 1/7] Allow accessing named declared members during a type's resolution --- tsc/internal/checker/checker.go | 80 +++++++++++++++++++++++++++++++-- 1 file changed, 77 insertions(+), 3 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index d689a6453e8a6..5d7d4da49db5d 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -552,6 +552,13 @@ type VarianceStackEntry struct { typeParameters []*Type } +type ResolvingObjectType struct { + t *Type // Type being resolved + mapper *TypeMapper // Instantiation mapper + baseTypes []*Type // Instantiated base types + resolvedMembers ast.SymbolTable // Members resolved by getDeclaredPropertyOfResolvingType +} + const maxSerializationLevel = 2 type Program interface { @@ -850,6 +857,7 @@ type Checker struct { awaitedTypeStack []*Type reverseMappedSourceStack []*Type reverseMappedTargetStack []*Type + resolvingObjectTypeStack []ResolvingObjectType reverseExpandingFlags ExpandingFlags freeRelater *Relater subtypeRelation *Relation @@ -19381,6 +19389,11 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr t = c.getReducedApparentType(t) switch { case t.flags&TypeFlagsObject != 0: + if t.objectFlags&ObjectFlagsMembersResolved == 0 && len(c.resolvingObjectTypeStack) != 0 { + if symbol := c.getDeclaredPropertyOfResolvingType(t, name); symbol != nil { + return symbol + } + } resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.getMember(name) if symbol != nil { @@ -19428,6 +19441,38 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr return nil } +// We allow accessing declared members of a class or interface while building its member layout. This +// means that base classes of a class or interface can access members declared in the class or interface +// during its resolution without causing a circularity error. Members resolved by this function are +// eventually propagated into the resolved members of the class or interface. +func (c *Checker) getDeclaredPropertyOfResolvingType(t *Type, name string) *ast.Symbol { + for i := len(c.resolvingObjectTypeStack) - 1; i >= 0; i-- { + r := &c.resolvingObjectTypeStack[i] + if r.t == t { + resolved := r.resolvedMembers.Get(name) + if resolved == nil { + if symbol := c.resolveDeclaredMembers(getTargetType(t)).declaredMembers.Get(name); symbol != nil { + resolved = c.instantiateSymbol(symbol, r.mapper) + } else { + for _, baseType := range r.baseTypes { + if resolved = c.getPropertyOfTypeEx(baseType, name, true, false); resolved != nil { + break + } + } + } + if resolved != nil { + if r.resolvedMembers == nil { + r.resolvedMembers = ast.NewSymbolTableWithCapacity(1) + } + r.resolvedMembers.Set(name, resolved) + } + } + return resolved + } + } + return nil +} + // Return the type of the given property in the given type, or nil if no such property exists func (c *Checker) getTypeOfPropertyOfType(t *Type, name string) *Type { prop := c.getPropertyOfType(t, name) @@ -19574,7 +19619,6 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) { var mapper *TypeMapper var typeParameters []*Type var typeArguments []*Type - var members ast.SymbolTable var callSignatures []*Signature var constructSignatures []*Signature var indexInfos []*IndexInfo @@ -19606,6 +19650,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) { instantiatedBaseTypes[i] = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) } } + c.resolvingObjectTypeStack = append(c.resolvingObjectTypeStack, ResolvingObjectType{t: t, mapper: mapper, baseTypes: instantiatedBaseTypes}) // Member layout must be computed before inherited signatures, which may circularly depend on it layout := c.getMemberLayout(t, instantiatedBaseTypes) for _, instantiatedBaseType := range instantiatedBaseTypes { @@ -19621,12 +19666,29 @@ func (c *Checker) resolveObjectTypeMembers(t *Type) { return findIndexInfo(indexInfos, info.keyType) == nil })) } - c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) + c.setStructuredTypeMembers(t, nil, callSignatures, constructSignatures, indexInfos) lazyState := c.lazyStateArena.New() lazyState.layout = layout lazyState.mapper = mapper lazyState.baseTypes = instantiatedBaseTypes t.AsTypeReference().lazy = lazyState + c.propagateResolvedDeclaredMembers() + c.resolvingObjectTypeStack = c.resolvingObjectTypeStack[:len(c.resolvingObjectTypeStack)-1] +} + +// Propagate members resolved by getDeclaredPropertyOfResolvingType +func (c *Checker) propagateResolvedDeclaredMembers() { + r := &c.resolvingObjectTypeStack[len(c.resolvingObjectTypeStack)-1] + s := r.t.AsTypeReference() + for symbol := range r.resolvedMembers.Values() { + if memberInfo := s.lazy.layout.memberInfoMap[symbol.Name()]; memberInfo != nil { + if s.properties == nil { + s.properties = make([]*ast.Symbol, len(s.lazy.layout.memberInfos)) + } + s.properties[memberInfo.slotIndex] = symbol + } + } + r.resolvedMembers = nil } func (c *Checker) getMemberLayout(t *Type, baseTypes []*Type) *MemberLayout { @@ -28117,10 +28179,22 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node } func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || + return t.flags&TypeFlagsObject != 0 && !c.isResolvingObjectType(t) && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType) } +func (c *Checker) isResolvingObjectType(t *Type) bool { + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + return false + } + for i := range c.resolvingObjectTypeStack { + if c.resolvingObjectTypeStack[i].t == t { + return true + } + } + return false +} + func (c *Checker) shouldDeferIndexedAccessType(objectType *Type, indexType *Type, accessNode *ast.Node) bool { if c.isGenericIndexType(indexType) { return true From 0a5b278bbf4f12f6f62f3c274c9e0eca8d206db6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Oct 2026 08:27:44 -0700 Subject: [PATCH 2/7] Accept new baselines --- .../compiler/mutuallyRecursiveInference.errors.txt | 5 +---- ...aredMemberLayoutThisIntersectionBase.errors.txt | 14 +------------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/tsc/testdata/baselines/reference/compiler/mutuallyRecursiveInference.errors.txt b/tsc/testdata/baselines/reference/compiler/mutuallyRecursiveInference.errors.txt index f267690f34e18..5b6b3f6f75a76 100644 --- a/tsc/testdata/baselines/reference/compiler/mutuallyRecursiveInference.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/mutuallyRecursiveInference.errors.txt @@ -1,10 +1,9 @@ mutuallyRecursiveInference.ts(2,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. mutuallyRecursiveInference.ts(9,5): error TS2564: Property 'a' has no initializer and is not definitely assigned in the constructor. mutuallyRecursiveInference.ts(10,5): error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor. -mutuallyRecursiveInference.ts(12,9): error TS5114: Instantiations of type 'T' appear infinitely circular. -==== mutuallyRecursiveInference.ts (4 errors) ==== +==== mutuallyRecursiveInference.ts (3 errors) ==== class T { a: A; ~ @@ -23,8 +22,6 @@ mutuallyRecursiveInference.ts(12,9): error TS5114: Instantiations of type 'T' ap !!! error TS2564: Property 'b' has no initializer and is not definitely assigned in the constructor. m2() { this.a - ~~~~~~ -!!! error TS5114: Instantiations of type 'T' appear infinitely circular. } } \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/sharedMemberLayoutThisIntersectionBase.errors.txt b/tsc/testdata/baselines/reference/compiler/sharedMemberLayoutThisIntersectionBase.errors.txt index 38ea8e221e2e3..b56bf6531dd0a 100644 --- a/tsc/testdata/baselines/reference/compiler/sharedMemberLayoutThisIntersectionBase.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/sharedMemberLayoutThisIntersectionBase.errors.txt @@ -1,34 +1,22 @@ -sharedMemberLayoutThisIntersectionBase.ts(5,11): error TS2589: Type instantiation is excessively deep and possibly infinite. -sharedMemberLayoutThisIntersectionBase.ts(7,1): error TS2589: Type instantiation is excessively deep and possibly infinite. sharedMemberLayoutThisIntersectionBase.ts(7,3): error TS2339: Property 'm' does not exist on type 'D<"b">'. -sharedMemberLayoutThisIntersectionBase.ts(10,7): error TS2589: Type instantiation is excessively deep and possibly infinite. -sharedMemberLayoutThisIntersectionBase.ts(12,1): error TS2589: Type instantiation is excessively deep and possibly infinite. sharedMemberLayoutThisIntersectionBase.ts(12,3): error TS2339: Property 'm' does not exist on type 'C<"b">'. -==== sharedMemberLayoutThisIntersectionBase.ts (6 errors) ==== +==== sharedMemberLayoutThisIntersectionBase.ts (2 errors) ==== interface A { kind: string; m: this["kind"] } interface B { m: "a" } type AB = A & B; interface D extends AB { kind: T } - ~ -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. declare let d: D<"b">; d.m; - ~~~ -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~ !!! error TS2339: Property 'm' does not exist on type 'D<"b">'. declare const ABCtor: new () => AB; class C extends ABCtor { declare kind: T } - ~ -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. declare let c: C<"b">; c.m; - ~~~ -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. ~ !!! error TS2339: Property 'm' does not exist on type 'C<"b">'. From d59ed3f929c98850b9370e2817a756037c083b11 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Oct 2026 08:44:59 -0700 Subject: [PATCH 3/7] Add regression tests --- .../circularDeclaredPropertyAccess.symbols | 102 ++++++++++++++++++ .../circularDeclaredPropertyAccess.types | 54 ++++++++++ .../circularDeclaredPropertyAccess.ts | 25 +++++ 3 files changed, 181 insertions(+) create mode 100644 tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.types create mode 100644 tsc/testdata/tests/cases/compiler/circularDeclaredPropertyAccess.ts diff --git a/tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.symbols b/tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.symbols new file mode 100644 index 0000000000000..a05b24d6aca1c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.symbols @@ -0,0 +1,102 @@ +//// [tests/cases/compiler/circularDeclaredPropertyAccess.ts] //// + +=== circularDeclaredPropertyAccess.ts === +// https://github.com/microsoft/TypeScript/issues/64605 + +interface Internals { input: I } +>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0)) +>I : Symbol(I, Decl(circularDeclaredPropertyAccess.ts, 2, 20)) +>input : Symbol(Internals.input, Decl(circularDeclaredPropertyAccess.ts, 2, 34)) +>I : Symbol(I, Decl(circularDeclaredPropertyAccess.ts, 2, 20)) + +interface Schema { _zod: Internals } +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>_zod : Symbol(Schema._zod, Decl(circularDeclaredPropertyAccess.ts, 3, 18)) +>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0)) + +type RecordInput = V extends unknown ? Record : never; +>RecordInput : Symbol(RecordInput, Decl(circularDeclaredPropertyAccess.ts, 3, 36)) +>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 4, 17)) +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 4, 17)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 4, 17)) + +interface RecordSchema extends Schema { _zod: Internals> } +>RecordSchema : Symbol(RecordSchema, Decl(circularDeclaredPropertyAccess.ts, 4, 100)) +>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 5, 23)) +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>_zod : Symbol(RecordSchema._zod, Decl(circularDeclaredPropertyAccess.ts, 5, 57)) +>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0)) +>RecordInput : Symbol(RecordInput, Decl(circularDeclaredPropertyAccess.ts, 3, 36)) +>V : Symbol(V, Decl(circularDeclaredPropertyAccess.ts, 5, 23)) + +interface UnionInternals extends Internals {} +>UnionInternals : Symbol(UnionInternals, Decl(circularDeclaredPropertyAccess.ts, 5, 91)) +>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 6, 25)) +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>Internals : Symbol(Internals, Decl(circularDeclaredPropertyAccess.ts, 0, 0)) +>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 6, 25)) + +interface UnionSchema extends Schema { _zod: UnionInternals } +>UnionSchema : Symbol(UnionSchema, Decl(circularDeclaredPropertyAccess.ts, 6, 102)) +>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 7, 22)) +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>Schema : Symbol(Schema, Decl(circularDeclaredPropertyAccess.ts, 2, 45)) +>_zod : Symbol(UnionSchema._zod, Decl(circularDeclaredPropertyAccess.ts, 7, 67)) +>UnionInternals : Symbol(UnionInternals, Decl(circularDeclaredPropertyAccess.ts, 5, 91)) +>T : Symbol(T, Decl(circularDeclaredPropertyAccess.ts, 7, 22)) + +type JsonValue = { [k: string]: JsonValue }; +>JsonValue : Symbol(JsonValue, Decl(circularDeclaredPropertyAccess.ts, 7, 93)) +>k : Symbol(k, Decl(circularDeclaredPropertyAccess.ts, 8, 20)) +>JsonValue : Symbol(JsonValue, Decl(circularDeclaredPropertyAccess.ts, 7, 93)) + +type _Json = UnionSchema<[RecordSchema]>; +>_Json : Symbol(_Json, Decl(circularDeclaredPropertyAccess.ts, 8, 44)) +>UnionSchema : Symbol(UnionSchema, Decl(circularDeclaredPropertyAccess.ts, 6, 102)) +>RecordSchema : Symbol(RecordSchema, Decl(circularDeclaredPropertyAccess.ts, 4, 100)) +>Json : Symbol(Json, Decl(circularDeclaredPropertyAccess.ts, 11, 67)) + +type _JsonInternals = _Json["_zod"]; +>_JsonInternals : Symbol(_JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 9, 47)) +>_Json : Symbol(_Json, Decl(circularDeclaredPropertyAccess.ts, 8, 44)) + +interface JsonInternals extends _JsonInternals { input: JsonValue } +>JsonInternals : Symbol(JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 10, 36)) +>_JsonInternals : Symbol(_JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 9, 47)) +>input : Symbol(JsonInternals.input, Decl(circularDeclaredPropertyAccess.ts, 11, 48)) +>JsonValue : Symbol(JsonValue, Decl(circularDeclaredPropertyAccess.ts, 7, 93)) + +interface Json extends _Json { _zod: JsonInternals } +>Json : Symbol(Json, Decl(circularDeclaredPropertyAccess.ts, 11, 67)) +>_Json : Symbol(_Json, Decl(circularDeclaredPropertyAccess.ts, 8, 44)) +>_zod : Symbol(Json._zod, Decl(circularDeclaredPropertyAccess.ts, 12, 30)) +>JsonInternals : Symbol(JsonInternals, Decl(circularDeclaredPropertyAccess.ts, 10, 36)) + +// Ensure getDeclaredPropertyOfResolvingType works for inherited declared members + +interface A { kind: string; m: this["kind"] } +>A : Symbol(A, Decl(circularDeclaredPropertyAccess.ts, 12, 52)) +>kind : Symbol(A.kind, Decl(circularDeclaredPropertyAccess.ts, 16, 13)) +>m : Symbol(A.m, Decl(circularDeclaredPropertyAccess.ts, 16, 27)) + +interface B { m: "a" } +>B : Symbol(B, Decl(circularDeclaredPropertyAccess.ts, 16, 45)) +>m : Symbol(B.m, Decl(circularDeclaredPropertyAccess.ts, 17, 13)) + +type AB = A & B; +>AB : Symbol(AB, Decl(circularDeclaredPropertyAccess.ts, 17, 22)) +>A : Symbol(A, Decl(circularDeclaredPropertyAccess.ts, 12, 52)) +>B : Symbol(B, Decl(circularDeclaredPropertyAccess.ts, 16, 45)) + +interface D extends AB { kind: "a" } +>D : Symbol(D, Decl(circularDeclaredPropertyAccess.ts, 18, 16)) +>AB : Symbol(AB, Decl(circularDeclaredPropertyAccess.ts, 17, 22)) +>kind : Symbol(D.kind, Decl(circularDeclaredPropertyAccess.ts, 20, 24)) + +interface E extends D {} +>E : Symbol(E, Decl(circularDeclaredPropertyAccess.ts, 20, 36)) +>D : Symbol(D, Decl(circularDeclaredPropertyAccess.ts, 18, 16)) + diff --git a/tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.types b/tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.types new file mode 100644 index 0000000000000..911ec7f3388a0 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/circularDeclaredPropertyAccess.types @@ -0,0 +1,54 @@ +//// [tests/cases/compiler/circularDeclaredPropertyAccess.ts] //// + +=== circularDeclaredPropertyAccess.ts === +// https://github.com/microsoft/TypeScript/issues/64605 + +interface Internals { input: I } +>input : I + +interface Schema { _zod: Internals } +>_zod : Internals + +type RecordInput = V extends unknown ? Record : never; +>RecordInput : RecordInput + +interface RecordSchema extends Schema { _zod: Internals> } +>_zod : Internals> + +interface UnionInternals extends Internals {} +interface UnionSchema extends Schema { _zod: UnionInternals } +>_zod : UnionInternals + +type JsonValue = { [k: string]: JsonValue }; +>JsonValue : JsonValue +>k : string + +type _Json = UnionSchema<[RecordSchema]>; +>_Json : _Json + +type _JsonInternals = _Json["_zod"]; +>_JsonInternals : UnionInternals<[RecordSchema]> + +interface JsonInternals extends _JsonInternals { input: JsonValue } +>input : JsonValue + +interface Json extends _Json { _zod: JsonInternals } +>_zod : JsonInternals + +// Ensure getDeclaredPropertyOfResolvingType works for inherited declared members + +interface A { kind: string; m: this["kind"] } +>kind : string +>m : this["kind"] + +interface B { m: "a" } +>m : "a" + +type AB = A & B; +>AB : AB + +interface D extends AB { kind: "a" } +>kind : "a" + +interface E extends D {} + diff --git a/tsc/testdata/tests/cases/compiler/circularDeclaredPropertyAccess.ts b/tsc/testdata/tests/cases/compiler/circularDeclaredPropertyAccess.ts new file mode 100644 index 0000000000000..cd60247771008 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/circularDeclaredPropertyAccess.ts @@ -0,0 +1,25 @@ +// @noEmit: true + +// https://github.com/microsoft/TypeScript/issues/64605 + +interface Internals { input: I } +interface Schema { _zod: Internals } +type RecordInput = V extends unknown ? Record : never; +interface RecordSchema extends Schema { _zod: Internals> } +interface UnionInternals extends Internals {} +interface UnionSchema extends Schema { _zod: UnionInternals } +type JsonValue = { [k: string]: JsonValue }; +type _Json = UnionSchema<[RecordSchema]>; +type _JsonInternals = _Json["_zod"]; +interface JsonInternals extends _JsonInternals { input: JsonValue } +interface Json extends _Json { _zod: JsonInternals } + +// Ensure getDeclaredPropertyOfResolvingType works for inherited declared members + +interface A { kind: string; m: this["kind"] } +interface B { m: "a" } +type AB = A & B; + +interface D extends AB { kind: "a" } + +interface E extends D {} From f5973655ed21bbaf66d65c91ca13769f6b9f2660 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Oct 2026 12:21:29 -0700 Subject: [PATCH 4/7] Don't compute address of array element that might move --- tsc/internal/checker/checker.go | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 5d7d4da49db5d..500a6f48e401d 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19447,24 +19447,23 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr // eventually propagated into the resolved members of the class or interface. func (c *Checker) getDeclaredPropertyOfResolvingType(t *Type, name string) *ast.Symbol { for i := len(c.resolvingObjectTypeStack) - 1; i >= 0; i-- { - r := &c.resolvingObjectTypeStack[i] - if r.t == t { - resolved := r.resolvedMembers.Get(name) + if c.resolvingObjectTypeStack[i].t == t { + resolved := c.resolvingObjectTypeStack[i].resolvedMembers.Get(name) if resolved == nil { if symbol := c.resolveDeclaredMembers(getTargetType(t)).declaredMembers.Get(name); symbol != nil { - resolved = c.instantiateSymbol(symbol, r.mapper) + resolved = c.instantiateSymbol(symbol, c.resolvingObjectTypeStack[i].mapper) } else { - for _, baseType := range r.baseTypes { + for _, baseType := range c.resolvingObjectTypeStack[i].baseTypes { if resolved = c.getPropertyOfTypeEx(baseType, name, true, false); resolved != nil { break } } } if resolved != nil { - if r.resolvedMembers == nil { - r.resolvedMembers = ast.NewSymbolTableWithCapacity(1) + if c.resolvingObjectTypeStack[i].resolvedMembers == nil { + c.resolvingObjectTypeStack[i].resolvedMembers = ast.NewSymbolTableWithCapacity(1) } - r.resolvedMembers.Set(name, resolved) + c.resolvingObjectTypeStack[i].resolvedMembers.Set(name, resolved) } } return resolved From 96bc57fd0a233be52d635d7d5b52d14452bd9cc6 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Oct 2026 16:35:06 -0700 Subject: [PATCH 5/7] Exclude class/interface types from isStringIndexSignatureOnlyType --- tsc/internal/checker/checker.go | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 500a6f48e401d..e255fef6afe2f 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -28178,22 +28178,10 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node } func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isResolvingObjectType(t) && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || + return t.flags&TypeFlagsObject != 0 && t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsReference) == 0 && !c.isGenericMappedType(t) && len(c.getLayoutPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType) } -func (c *Checker) isResolvingObjectType(t *Type) bool { - if t.objectFlags&ObjectFlagsMembersResolved != 0 { - return false - } - for i := range c.resolvingObjectTypeStack { - if c.resolvingObjectTypeStack[i].t == t { - return true - } - } - return false -} - func (c *Checker) shouldDeferIndexedAccessType(objectType *Type, indexType *Type, accessNode *ast.Node) bool { if c.isGenericIndexType(indexType) { return true From 7000c3c689090bde954a0a0ab9b954139873df04 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Fri, 30 Oct 2026 16:35:12 -0700 Subject: [PATCH 6/7] Accept new baselines --- .../recursiveIndexedAccessSimplification.errors.txt | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/tsc/testdata/baselines/reference/compiler/recursiveIndexedAccessSimplification.errors.txt b/tsc/testdata/baselines/reference/compiler/recursiveIndexedAccessSimplification.errors.txt index 3567f2da71f6a..8c689e15ab0bb 100644 --- a/tsc/testdata/baselines/reference/compiler/recursiveIndexedAccessSimplification.errors.txt +++ b/tsc/testdata/baselines/reference/compiler/recursiveIndexedAccessSimplification.errors.txt @@ -1,4 +1,3 @@ -recursiveIndexedAccessSimplification.ts(5,5): error TS4109: Type arguments for 'Array' circularly reference themselves. recursiveIndexedAccessSimplification.ts(5,9): error TS2536: Type 'number' cannot be used to index type 'Recur'. recursiveIndexedAccessSimplification.ts(8,5): error TS2322: Type '[string, ...Recur[]]' is not assignable to type 'Recur'. Type '[string, ...Recur[]]' is not assignable to type 'Recur[number][]'. @@ -6,18 +5,14 @@ recursiveIndexedAccessSimplification.ts(8,5): error TS2322: Type '[string, ...Re Type 'string' is not assignable to type 'Recur[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur | undefined; })[number]'. Type 'string' is not assignable to type 'Recur[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur | undefined; })[number]'. Type 'string' is not assignable to type '(T extends unknown[] ? {} : { [K in keyof T]?: Recur | undefined; })[number]'. -recursiveIndexedAccessSimplification.ts(8,12): error TS2589: Type instantiation is excessively deep and possibly infinite. -recursiveIndexedAccessSimplification.ts(12,37): error TS2589: Type instantiation is excessively deep and possibly infinite. -==== recursiveIndexedAccessSimplification.ts (5 errors) ==== +==== recursiveIndexedAccessSimplification.ts (2 errors) ==== // https://github.com/microsoft/TypeScript/issues/63270 type Recur = (T extends (unknown[]) ? {} : { [K in keyof T]?: Recur}) | [...Recur[number][]]; - ~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS4109: Type arguments for 'Array' circularly reference themselves. ~~~~~~~~~~~~~~~~ !!! error TS2536: Type 'number' cannot be used to index type 'Recur'. @@ -30,13 +25,9 @@ recursiveIndexedAccessSimplification.ts(12,37): error TS2589: Type instantiation !!! error TS2322: Type 'string' is not assignable to type 'Recur[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur | undefined; })[number]'. !!! error TS2322: Type 'string' is not assignable to type 'Recur[number] & (T extends unknown[] ? {} : { [K in keyof T]?: Recur | undefined; })[number]'. !!! error TS2322: Type 'string' is not assignable to type '(T extends unknown[] ? {} : { [K in keyof T]?: Recur | undefined; })[number]'. - ~~~~~~~~~~~~~~~~ -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. } function a(l: Recur[]): void { const x: Recur | undefined = join(l); - ~~~~~~~ -!!! error TS2589: Type instantiation is excessively deep and possibly infinite. } \ No newline at end of file From 6ced52cf64612114c44bfa99f17b3c818384bf87 Mon Sep 17 00:00:00 2001 From: Anders Hejlsberg Date: Sat, 31 Oct 2026 15:19:17 -0700 Subject: [PATCH 7/7] Exclude reserved and private names --- tsc/internal/checker/checker.go | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index e255fef6afe2f..b32d5cdc5a092 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19450,11 +19450,12 @@ func (c *Checker) getDeclaredPropertyOfResolvingType(t *Type, name string) *ast. if c.resolvingObjectTypeStack[i].t == t { resolved := c.resolvingObjectTypeStack[i].resolvedMembers.Get(name) if resolved == nil { - if symbol := c.resolveDeclaredMembers(getTargetType(t)).declaredMembers.Get(name); symbol != nil { + if symbol := c.resolveDeclaredMembers(getTargetType(t)).declaredMembers.Get(name); symbol != nil && c.isNamedMember(symbol, name) { resolved = c.instantiateSymbol(symbol, c.resolvingObjectTypeStack[i].mapper) } else { for _, baseType := range c.resolvingObjectTypeStack[i].baseTypes { - if resolved = c.getPropertyOfTypeEx(baseType, name, true, false); resolved != nil { + if symbol := c.getPropertyOfTypeEx(baseType, name, true, false); symbol != nil && !isStaticPrivateIdentifierProperty(symbol) { + resolved = symbol break } }