From 85518a5c0624cdab871b149a684e13e5e6b9ed9a Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Sat, 26 Sep 2026 19:44:18 -0400 Subject: [PATCH 1/9] Resolve members of instantiated class and interface references lazily Looking up one property of an instantiated class or interface reference (`expect(x).toBe`, `schema.optional`, `arr.map`) resolved all of its members: every declared member was instantiated and every inherited member merged, although most of those symbols are never used. The first lookup on such a reference now prepares a lazy member table instead. Preparing does everything resolveObjectTypeMembers does except create the member symbols, in the same order, so everything that is resolved along the way (signatures, index infos, base types and their members) is resolved exactly as before. It also records which declared members instantiateSymbol would return as they are at that point, since that depends on what has been resolved. Lookups then instantiate only the requested member, walking the prepared base types in addInheritedMembers order, and resolving the members in full later reuses the symbols already handed out. If preparing leads back to the reference, it exposes the same partial members resolveObjectTypeMembers would, and is resolved in full from then on. A lookup of a missing name answers the Object/Function augmentation from the number of call and construct signatures, counted from the declared signatures and those of the prepared base types. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 66 +++- tsc/internal/checker/lazymembers.go | 350 ++++++++++++++++++ ...stantiatedReferenceMemberLookup.errors.txt | 108 ++++++ .../instantiatedReferenceMemberLookup.symbols | 275 ++++++++++++++ .../instantiatedReferenceMemberLookup.types | 245 ++++++++++++ .../instantiatedReferenceMemberLookup.ts | 85 +++++ 6 files changed, 1118 insertions(+), 11 deletions(-) create mode 100644 tsc/internal/checker/lazymembers.go create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types create mode 100644 tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 845f62365bb84..ca0379b1984d3 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -701,6 +701,7 @@ type Checker struct { sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] regExpScanner *scanner.Scanner patternForType map[*Type]*ast.Node + lazyMemberTables map[*Type]*lazyMemberTable contextFreeTypes map[*ast.Node]*Type anyType *Type autoType *Type @@ -981,6 +982,7 @@ func NewChecker(program Program, tracer *Tracer) (*Checker, *sync.Mutex) { c.propertiesTypes = make(map[PropertiesTypesKey]*Type) c.mergedSymbols = make(map[*ast.Symbol]*ast.Symbol) c.patternForType = make(map[*Type]*ast.Node) + c.lazyMemberTables = make(map[*Type]*lazyMemberTable) c.contextFreeTypes = make(map[*ast.Node]*Type) c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") c.autoType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsNonInferrableType) @@ -19238,6 +19240,31 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr t = c.getReducedApparentType(t) switch { case t.flags&TypeFlagsObject != 0: + if symbol, ok := c.lookupMemberLazily(t, name); ok { + if symbol != nil && c.symbolIsValueEx(symbol, includeTypeOnlyMembers) { + return symbol + } + if skipObjectFunctionPropertyAugment { + return nil + } + if symbol == nil { + if shape := c.getLazyShape(t); shape != nil { + var functionType *Type + switch { + case shape.callSignatureCount != 0: + functionType = c.globalCallableFunctionType + case shape.constructSignatureCount != 0: + functionType = c.globalNewableFunctionType + } + if functionType != nil { + if symbol := c.getPropertyOfObjectType(functionType, name); symbol != nil { + return symbol + } + } + return c.getPropertyOfObjectType(c.globalObjectType, name) + } + } + } resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] if symbol != nil { @@ -19431,13 +19458,12 @@ func (c *Checker) resolveClassOrInterfaceMembers(t *Type) { } func (c *Checker) resolveTypeReferenceMembers(t *Type) { - source := t.Target() - typeParameters := source.AsInterfaceType().allTypeParameters - typeArguments := c.getTypeArguments(t) - paddedTypeArguments := typeArguments - if len(typeArguments) == len(typeParameters)-1 { - paddedTypeArguments = core.Concatenate(typeArguments, []*Type{t}) + if lm := c.lazyMemberTables[t]; lm != nil && lm.state != lazyMembersResolvingDeclared { + c.resolveLazyMembers(t, lm) + return } + source := t.Target() + typeParameters, paddedTypeArguments := c.getReferenceMemberTypeArguments(t, source) c.resolveObjectTypeMembers(t, source, typeParameters, paddedTypeArguments) } @@ -21089,24 +21115,36 @@ func (c *Checker) instantiateSymbolTable(symbols ast.SymbolTable, m *TypeMapper) } func (c *Checker) instantiateSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symbol { - if symbol == nil { - return nil + if symbol == nil || c.isSymbolUnaffectedByInstantiation(symbol, m) { + return symbol } + return c.newInstantiatedSymbol(symbol, m) +} + +// isSymbolUnaffectedByInstantiation reports whether instantiateSymbol returns +// the symbol itself. That depends on whether the type of the symbol has been +// resolved, so the answer can change from false to true. +func (c *Checker) isSymbolUnaffectedByInstantiation(symbol *ast.Symbol, m *TypeMapper) bool { links := c.valueSymbolLinks.Get(symbol) if m != nil && m.MapsThisOnly() && isThisless(symbol) { - return symbol + return true } // If the type of the symbol is already resolved, and if that type could not possibly // be affected by instantiation, simply return the symbol itself. if links.resolvedType != nil && !c.couldContainTypeVariables(links.resolvedType) { if symbol.Flags&ast.SymbolFlagsSetAccessor == 0 { - return symbol + return true } // If we're a setter, check writeType. if links.writeType != nil && !c.couldContainTypeVariables(links.writeType) { - return symbol + return true } } + return false +} + +func (c *Checker) newInstantiatedSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symbol { + links := c.valueSymbolLinks.Get(symbol) if symbol.CheckFlags&ast.CheckFlagsInstantiated != 0 { // If symbol being instantiated is itself a instantiation, fetch the original target and combine the // type mappers. This ensures that original type identities are properly preserved and that aliases @@ -21740,6 +21778,12 @@ func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, in */ func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { + if symbol, ok := c.lookupMemberLazily(t, name); ok { + if symbol != nil && c.symbolIsValue(symbol) { + return symbol + } + return nil + } resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] if symbol != nil && c.symbolIsValue(symbol) { diff --git a/tsc/internal/checker/lazymembers.go b/tsc/internal/checker/lazymembers.go new file mode 100644 index 0000000000000..dfb3b1bcfb29c --- /dev/null +++ b/tsc/internal/checker/lazymembers.go @@ -0,0 +1,350 @@ +package checker + +import ( + "slices" + + "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/core" +) + +// Resolving the members of an instantiated class or interface reference +// instantiates every declared member and merges every inherited one, even when +// the caller only wants one property (`expect(x).toBe`, `schema.optional`, +// `arr.map`). In large programs most of those symbols are never used. +// +// Instead, the first lookup on such a reference prepares a lazy member table: +// it does everything resolveObjectTypeMembers would do except create the +// member symbols, in the same order, so everything that gets resolved along +// the way (signatures, index infos, base types and their members) is resolved +// exactly as before. Lookups then instantiate only the requested member, the +// same way resolveObjectTypeMembers would have at the time the table was +// prepared, and if the members are resolved in full later, the symbols +// already handed out are reused. Should preparing the table lead back to the +// reference, the reference exposes the same partial members that +// resolveObjectTypeMembers would, and is from then on resolved in full. + +type lazyMembersState int + +const ( + lazyMembersResolvingDeclared lazyMembersState = iota // instantiating declared signatures and index infos, resolving base types + lazyMembersResolvingBases // instantiating base types and resolving their members + lazyMembersReady + lazyMembersMaterialized // resolving the members in full, as resolveObjectTypeMembers does +) + +type lazyMemberTable struct { + state lazyMembersState + mapper *TypeMapper + typeArguments []*Type + unaffected []string // declared members that instantiate to themselves + callSignatures []*Signature + constructSignatures []*Signature + indexInfos []*IndexInfo + baseTypes []*Type // instantiated base types + inheritedCount int // number of base types whose members have been inherited + declared map[string]*ast.Symbol + found map[string]*ast.Symbol // memoized lookups; nil means no such member + resolving *resolvingMembers + shape *lazyShape +} + +// lazyShape describes the members of a reference whose lazy member table is +// ready, as far as that is known without resolving them. +type lazyShape struct { + callSignatureCount int + constructSignatureCount int +} + +// resolvingMembers holds the members of a lazily prepared type that is being +// resolved in full. +type resolvingMembers struct { + members ast.SymbolTable + callSignatures []*Signature + constructSignatures []*Signature + indexInfos []*IndexInfo +} + +// isUnresolvedInstantiatedReference reports whether t is a reference to a +// generic class or interface (other than the declaration itself) whose members +// haven't been resolved. +func isUnresolvedInstantiatedReference(t *Type) bool { + if t.flags&TypeFlagsObject == 0 || t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) != ObjectFlagsReference { + return false + } + source := t.Target() + if source == nil || source == t || source.objectFlags&ObjectFlagsClassOrInterface == 0 || source.objectFlags&ObjectFlagsTuple != 0 { + return false + } + return t.symbol == nil || t.symbol.Flags&ast.SymbolFlagsValueModule == 0 +} + +func (c *Checker) getReferenceMemberTypeArguments(t *Type, source *Type) (typeParameters []*Type, typeArguments []*Type) { + typeParameters = source.AsInterfaceType().allTypeParameters + typeArguments = c.getTypeArguments(t) + if len(typeArguments) == len(typeParameters)-1 { + typeArguments = core.Concatenate(typeArguments, []*Type{t}) + } + return typeParameters, typeArguments +} + +// getLazyMemberTable returns the lazy member table of t, preparing it first if +// needed, or nil if t's members aren't resolved lazily. A table returned while +// it is still being prepared isn't ready. +func (c *Checker) getLazyMemberTable(t *Type) *lazyMemberTable { + if !isUnresolvedInstantiatedReference(t) { + return nil + } + if lm := c.lazyMemberTables[t]; lm != nil { + return lm + } + typeParameters, typeArguments := c.getReferenceMemberTypeArguments(t, t.Target()) + if slices.Equal(typeParameters, typeArguments) { + return nil + } + // Resolving deferred type arguments may have led to t being resolved or + // prepared already. Like resolveObjectTypeMembers, which would then replace + // those members, prepare t (again) regardless. + lm := &lazyMemberTable{ + mapper: newTypeMapper(typeParameters, typeArguments), + typeArguments: typeArguments, + declared: map[string]*ast.Symbol{}, + found: map[string]*ast.Symbol{}, + } + c.lazyMemberTables[t] = lm + c.prepareLazyMembers(t, lm) + if lm.state != lazyMembersReady { + return nil + } + return lm +} + +// prepareLazyMembers mirrors resolveObjectTypeMembers, except that it only +// records which declared members would instantiate to themselves instead of +// instantiating them. +func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { + source := t.Target() + resolved := c.resolveDeclaredMembers(source) + for id, symbol := range resolved.declaredMembers { + if c.isNamedMember(symbol, id) && c.isSymbolUnaffectedByInstantiation(symbol, lm.mapper) { + lm.unaffected = append(lm.unaffected, id) + } + } + lm.callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, lm.mapper) + lm.constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, lm.mapper) + lm.indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, lm.mapper) + baseTypes := c.getBaseTypes(source) + lm.state = lazyMembersResolvingBases + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + // A lookup led to t being resolved in full; resolveObjectTypeMembers + // would now go on to replace those members with its own. + c.resolveLazyMembers(t, lm) + } + thisArgument := core.LastOrNil(lm.typeArguments) + for _, baseType := range baseTypes { + instantiatedBaseType := baseType + if thisArgument != nil { + instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, lm.mapper), thisArgument, false /*needsApparentType*/) + } + lm.baseTypes = append(lm.baseTypes, instantiatedBaseType) + if lm.state == lazyMembersResolvingBases && !c.prepareBaseTypeMembers(instantiatedBaseType) { + // The base type's members are only partially resolved, and t must + // inherit them as they are now. + c.resolveLazyMembers(t, lm) + } + if lm.state == lazyMembersMaterialized { + c.inheritBaseTypeMembers(lm.resolving, instantiatedBaseType) + } + lm.inheritedCount++ + } + if lm.state == lazyMembersMaterialized { + t.objectFlags &^= ObjectFlagsUnresolvedMembers + c.setStructuredTypeMembers(t, lm.resolving.members, lm.resolving.callSignatures, lm.resolving.constructSignatures, lm.resolving.indexInfos) + delete(c.lazyMemberTables, t) + return + } + lm.state = lazyMembersReady +} + +// prepareBaseTypeMembers resolves the members of an instantiated base type as +// inheriting from it in resolveObjectTypeMembers would. It returns false, and +// leaves that to the caller, when those members are only partially resolved. +func (c *Checker) prepareBaseTypeMembers(baseType *Type) bool { + if baseType == c.anyType { + return true + } + reduced := c.getReducedApparentType(baseType) + if reduced.flags&TypeFlagsObject != 0 { + if reduced.objectFlags&ObjectFlagsUnresolvedMembers != 0 { + return false + } + if lm := c.getLazyMemberTable(reduced); lm != nil { + return lm.state == lazyMembersReady + } + } + c.getPropertiesOfType(baseType) + c.getSignaturesOfType(baseType, SignatureKindCall) + c.getSignaturesOfType(baseType, SignatureKindConstruct) + c.getIndexInfosOfType(baseType) + return true +} + +// resolveLazyMembers resolves the members of t, whose lazy member table is +// ready, as resolveObjectTypeMembers does. While the table's base types are +// still being prepared, it only exposes the members inherited so far, like +// resolveObjectTypeMembers would at that point, and prepareLazyMembers +// finishes the job. +func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { + source := t.Target() + resolved := c.resolveDeclaredMembers(source) + var members ast.SymbolTable + if len(resolved.declaredMembers) != 0 { + members = make(ast.SymbolTable, len(resolved.declaredMembers)) + for id, symbol := range resolved.declaredMembers { + if c.isNamedMember(symbol, id) { + members[id] = c.getLazyDeclaredMember(lm, symbol, id) + } + } + } + r := &resolvingMembers{ + members: members, + callSignatures: lm.callSignatures, + constructSignatures: lm.constructSignatures, + indexInfos: lm.indexInfos, + } + if len(c.getBaseTypes(source)) != 0 { + c.setStructuredTypeMembers(t, r.members, r.callSignatures, r.constructSignatures, r.indexInfos) + t.objectFlags |= ObjectFlagsUnresolvedMembers + for _, baseType := range lm.baseTypes[:lm.inheritedCount] { + c.inheritBaseTypeMembers(r, baseType) + } + } + if lm.state != lazyMembersReady { + lm.state = lazyMembersMaterialized + lm.resolving = r + return + } + t.objectFlags &^= ObjectFlagsUnresolvedMembers + c.setStructuredTypeMembers(t, r.members, r.callSignatures, r.constructSignatures, r.indexInfos) + delete(c.lazyMemberTables, t) +} + +// inheritBaseTypeMembers is the body of the base type loop in +// resolveObjectTypeMembers. +func (c *Checker) inheritBaseTypeMembers(r *resolvingMembers, baseType *Type) { + r.members = c.addInheritedMembers(r.members, c.getPropertiesOfType(baseType)) + r.callSignatures = core.Concatenate(r.callSignatures, c.getSignaturesOfType(baseType, SignatureKindCall)) + r.constructSignatures = core.Concatenate(r.constructSignatures, c.getSignaturesOfType(baseType, SignatureKindConstruct)) + var inheritedIndexInfos []*IndexInfo + if baseType != c.anyType { + inheritedIndexInfos = c.getIndexInfosOfType(baseType) + } else { + inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} + } + r.indexInfos = core.Concatenate(r.indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { + return findIndexInfo(r.indexInfos, info.keyType) == nil + })) +} + +// getLazyDeclaredMember instantiates the declared member symbol named name as +// resolveObjectTypeMembers would have when the table was prepared. +func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { + result := lm.declared[name] + if result == nil { + if slices.Contains(lm.unaffected, name) { + result = symbol + } else { + result = c.newInstantiatedSymbol(symbol, lm.mapper) + } + lm.declared[name] = result + } + return result +} + +// lookupMemberLazily returns what resolveStructuredTypeMembers(t).members[name] +// would hold, without building t's member table. ok is false when the lazy +// path can't answer and the caller must resolve members normally. +func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, ok bool) { + if isReservedMemberName(name) { + return nil, false + } + lm := c.getLazyMemberTable(t) + if lm == nil || lm.state == lazyMembersResolvingDeclared { + return nil, false + } + if symbol, ok := lm.found[name]; ok { + return symbol, true + } + // Mirrors resolveObjectTypeMembers: declared named members first, then + // inherited properties, where a base may only fill a missing or non-value + // entry (addInheritedMembers). + resolved := c.resolveDeclaredMembers(t.Target()) + var result *ast.Symbol + if decl := resolved.declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { + result = c.getLazyDeclaredMember(lm, decl, name) + } + baseTypes := lm.baseTypes[:lm.inheritedCount] + if lm.state != lazyMembersReady && len(resolved.declaredMembers) == 0 { + // While base types are being resolved, resolveObjectTypeMembers only + // exposes inherited members through the table of declared members. + baseTypes = nil + } + for _, baseType := range baseTypes { + if result != nil && result.Flags&ast.SymbolFlagsValue != 0 { + break + } + if prop := c.getNamedPropertyOfType(baseType, name); prop != nil && !isStaticPrivateIdentifierProperty(prop) { + result = prop + } + } + if lm.state == lazyMembersReady { + lm.found[name] = result + } + return result, true +} + +// getNamedPropertyOfType finds name among getPropertiesOfType(t). +func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { + reduced := c.getReducedApparentType(t) + if reduced.flags&TypeFlagsObject != 0 { + if symbol, ok := c.lookupMemberLazily(reduced, name); ok { + return symbol + } + if symbol := c.resolveStructuredTypeMembers(reduced).members[name]; symbol != nil && c.isNamedMember(symbol, name) { + return symbol + } + return nil + } + for _, prop := range c.getPropertiesOfType(t) { + if prop.Name == name { + return prop + } + } + return nil +} + +// getLazyShape returns the shape of t when t has a lazy member table that is +// ready. It is computed from the declared signatures and those of the +// prepared base types, which resolveObjectTypeMembers concatenates. +func (c *Checker) getLazyShape(t *Type) *lazyShape { + lm := c.getLazyMemberTable(t) + if lm == nil || lm.state != lazyMembersReady { + return nil + } + if lm.shape == nil { + shape := &lazyShape{ + callSignatureCount: len(lm.callSignatures), + constructSignatureCount: len(lm.constructSignatures), + } + for _, baseType := range lm.baseTypes { + if baseShape := c.getLazyShape(c.getReducedApparentType(baseType)); baseShape != nil { + shape.callSignatureCount += baseShape.callSignatureCount + shape.constructSignatureCount += baseShape.constructSignatureCount + } else { + shape.callSignatureCount += len(c.getSignaturesOfType(baseType, SignatureKindCall)) + shape.constructSignatureCount += len(c.getSignaturesOfType(baseType, SignatureKindConstruct)) + } + } + lm.shape = shape + } + return lm.shape +} diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt new file mode 100644 index 0000000000000..8bbd039be8d19 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt @@ -0,0 +1,108 @@ +instantiatedReferenceMemberLookup.ts(23,3): error TS2339: Property 'missing' does not exist on type 'Derived'. +instantiatedReferenceMemberLookup.ts(43,5): error TS2341: Property 'secret' is private and only accessible within class 'Box'. +instantiatedReferenceMemberLookup.ts(44,5): error TS2445: Property 'guarded' is protected and only accessible within class 'Box' and its subclasses. +instantiatedReferenceMemberLookup.ts(53,9): error TS2339: Property 'bind' does not exist on type 'Fn<"b">'. +instantiatedReferenceMemberLookup.ts(54,9): error TS2339: Property 'kind' does not exist on type 'Fn<"b">'. +instantiatedReferenceMemberLookup.ts(69,11): error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. + 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: string; }'. +instantiatedReferenceMemberLookup.ts(73,6): error TS2339: Property 'c' does not exist on type 'Wrap<{ a: string; b: number; }>'. + + +==== instantiatedReferenceMemberLookup.ts (7 errors) ==== + // Property lookups on instantiated class and interface references: declared + // and inherited members, members hidden by redeclarations, accessibility, + // `this` types, Object and Function members, and base types that change shape + // when instantiated. + + interface Base { + value: T; + method(): T; + shared: string; + } + + interface Derived extends Base { + shared: "derived"; + own: T; + } + + declare const d: Derived; + const d1 = d.value; + const d2 = d.method(); + const d3 = d.shared; + const d4 = d.own; + const d5 = d.toString(); + d.missing; + ~~~~~~~ +!!! error TS2339: Property 'missing' does not exist on type 'Derived'. + + class Box { + private secret!: T; + protected guarded!: T; + contents!: T; + self(): this { + return this; + } + } + + class NumberBox extends Box { + read() { + return this.guarded; + } + } + + declare const box: Box; + const box1 = box.contents; + const box2 = box.self().contents; + box.secret; + ~~~~~~ +!!! error TS2341: Property 'secret' is private and only accessible within class 'Box'. + box.guarded; + ~~~~~~~ +!!! error TS2445: Property 'guarded' is protected and only accessible within class 'Box' and its subclasses. + + declare const numberBox: NumberBox; + const numberBox1 = numberBox.self().read(); + + type Callable = { (): void; kind: T } & { kind: "a" }; + interface Fn extends Callable {} + + declare const neverFn: Fn<"b">; + neverFn.bind; + ~~~~ +!!! error TS2339: Property 'bind' does not exist on type 'Fn<"b">'. + neverFn.kind; + ~~~~ +!!! error TS2339: Property 'kind' does not exist on type 'Fn<"b">'. + + declare const fn: Fn<"a">; + const fn1 = fn.bind; + const fn2 = fn.kind; + + interface Newable { + new (): T; + } + interface NewableBox extends Newable> {} + + declare const newable: NewableBox; + const newable1 = newable.prototype; + const newable2 = new newable().contents; + + interface Wrap extends T {} + ~~~~ +!!! error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. +!!! error TS2430: 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: string; }'. + + declare const wrap: Wrap<{ a: string; b: number }>; + const wrap1 = wrap.b; + wrap.c; + ~ +!!! error TS2339: Property 'c' does not exist on type 'Wrap<{ a: string; b: number; }>'. + + interface Tree extends Array> { + value: T; + } + + declare const tree: Tree; + const tree1 = tree[0].value; + const tree2 = tree.map(child => child.value); + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols new file mode 100644 index 0000000000000..3001786ac5fcd --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols @@ -0,0 +1,275 @@ +//// [tests/cases/compiler/instantiatedReferenceMemberLookup.ts] //// + +=== instantiatedReferenceMemberLookup.ts === +// Property lookups on instantiated class and interface references: declared +// and inherited members, members hidden by redeclarations, accessibility, +// `this` types, Object and Function members, and base types that change shape +// when instantiated. + +interface Base { +>Base : Symbol(Base, Decl(instantiatedReferenceMemberLookup.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 5, 15)) + + value: T; +>value : Symbol(Base.value, Decl(instantiatedReferenceMemberLookup.ts, 5, 19)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 5, 15)) + + method(): T; +>method : Symbol(Base.method, Decl(instantiatedReferenceMemberLookup.ts, 6, 13)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 5, 15)) + + shared: string; +>shared : Symbol(Base.shared, Decl(instantiatedReferenceMemberLookup.ts, 7, 16)) +} + +interface Derived extends Base { +>Derived : Symbol(Derived, Decl(instantiatedReferenceMemberLookup.ts, 9, 1)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 11, 18)) +>Base : Symbol(Base, Decl(instantiatedReferenceMemberLookup.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 11, 18)) + + shared: "derived"; +>shared : Symbol(Derived.shared, Decl(instantiatedReferenceMemberLookup.ts, 11, 40)) + + own: T; +>own : Symbol(Derived.own, Decl(instantiatedReferenceMemberLookup.ts, 12, 22)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 11, 18)) +} + +declare const d: Derived; +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) +>Derived : Symbol(Derived, Decl(instantiatedReferenceMemberLookup.ts, 9, 1)) + +const d1 = d.value; +>d1 : Symbol(d1, Decl(instantiatedReferenceMemberLookup.ts, 17, 5)) +>d.value : Symbol(Base.value, Decl(instantiatedReferenceMemberLookup.ts, 5, 19)) +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) +>value : Symbol(Base.value, Decl(instantiatedReferenceMemberLookup.ts, 5, 19)) + +const d2 = d.method(); +>d2 : Symbol(d2, Decl(instantiatedReferenceMemberLookup.ts, 18, 5)) +>d.method : Symbol(Base.method, Decl(instantiatedReferenceMemberLookup.ts, 6, 13)) +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) +>method : Symbol(Base.method, Decl(instantiatedReferenceMemberLookup.ts, 6, 13)) + +const d3 = d.shared; +>d3 : Symbol(d3, Decl(instantiatedReferenceMemberLookup.ts, 19, 5)) +>d.shared : Symbol(Derived.shared, Decl(instantiatedReferenceMemberLookup.ts, 11, 40)) +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) +>shared : Symbol(Derived.shared, Decl(instantiatedReferenceMemberLookup.ts, 11, 40)) + +const d4 = d.own; +>d4 : Symbol(d4, Decl(instantiatedReferenceMemberLookup.ts, 20, 5)) +>d.own : Symbol(Derived.own, Decl(instantiatedReferenceMemberLookup.ts, 12, 22)) +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) +>own : Symbol(Derived.own, Decl(instantiatedReferenceMemberLookup.ts, 12, 22)) + +const d5 = d.toString(); +>d5 : Symbol(d5, Decl(instantiatedReferenceMemberLookup.ts, 21, 5)) +>d.toString : Symbol(Object.toString, Decl(lib.es5.d.ts, --, --)) +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) +>toString : Symbol(Object.toString, Decl(lib.es5.d.ts, --, --)) + +d.missing; +>d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) + +class Box { +>Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) + + private secret!: T; +>secret : Symbol(Box.secret, Decl(instantiatedReferenceMemberLookup.ts, 24, 14)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) + + protected guarded!: T; +>guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) + + contents!: T; +>contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) + + self(): this { +>self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) + + return this; +>this : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) + } +} + +class NumberBox extends Box { +>NumberBox : Symbol(NumberBox, Decl(instantiatedReferenceMemberLookup.ts, 31, 1)) +>Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) + + read() { +>read : Symbol(NumberBox.read, Decl(instantiatedReferenceMemberLookup.ts, 33, 37)) + + return this.guarded; +>this.guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) +>this : Symbol(NumberBox, Decl(instantiatedReferenceMemberLookup.ts, 31, 1)) +>guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) + } +} + +declare const box: Box; +>box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) +>Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) + +const box1 = box.contents; +>box1 : Symbol(box1, Decl(instantiatedReferenceMemberLookup.ts, 40, 5)) +>box.contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) +>box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) +>contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) + +const box2 = box.self().contents; +>box2 : Symbol(box2, Decl(instantiatedReferenceMemberLookup.ts, 41, 5)) +>box.self().contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) +>box.self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) +>box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) +>self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) +>contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) + +box.secret; +>box.secret : Symbol(Box.secret, Decl(instantiatedReferenceMemberLookup.ts, 24, 14)) +>box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) +>secret : Symbol(Box.secret, Decl(instantiatedReferenceMemberLookup.ts, 24, 14)) + +box.guarded; +>box.guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) +>box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) +>guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) + +declare const numberBox: NumberBox; +>numberBox : Symbol(numberBox, Decl(instantiatedReferenceMemberLookup.ts, 45, 13)) +>NumberBox : Symbol(NumberBox, Decl(instantiatedReferenceMemberLookup.ts, 31, 1)) + +const numberBox1 = numberBox.self().read(); +>numberBox1 : Symbol(numberBox1, Decl(instantiatedReferenceMemberLookup.ts, 46, 5)) +>numberBox.self().read : Symbol(NumberBox.read, Decl(instantiatedReferenceMemberLookup.ts, 33, 37)) +>numberBox.self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) +>numberBox : Symbol(numberBox, Decl(instantiatedReferenceMemberLookup.ts, 45, 13)) +>self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) +>read : Symbol(NumberBox.read, Decl(instantiatedReferenceMemberLookup.ts, 33, 37)) + +type Callable = { (): void; kind: T } & { kind: "a" }; +>Callable : Symbol(Callable, Decl(instantiatedReferenceMemberLookup.ts, 46, 43)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 48, 14)) +>kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 30)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 48, 14)) +>kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 44)) + +interface Fn extends Callable {} +>Fn : Symbol(Fn, Decl(instantiatedReferenceMemberLookup.ts, 48, 57)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 49, 13)) +>Callable : Symbol(Callable, Decl(instantiatedReferenceMemberLookup.ts, 46, 43)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 49, 13)) + +declare const neverFn: Fn<"b">; +>neverFn : Symbol(neverFn, Decl(instantiatedReferenceMemberLookup.ts, 51, 13)) +>Fn : Symbol(Fn, Decl(instantiatedReferenceMemberLookup.ts, 48, 57)) + +neverFn.bind; +>neverFn : Symbol(neverFn, Decl(instantiatedReferenceMemberLookup.ts, 51, 13)) + +neverFn.kind; +>neverFn : Symbol(neverFn, Decl(instantiatedReferenceMemberLookup.ts, 51, 13)) + +declare const fn: Fn<"a">; +>fn : Symbol(fn, Decl(instantiatedReferenceMemberLookup.ts, 55, 13)) +>Fn : Symbol(Fn, Decl(instantiatedReferenceMemberLookup.ts, 48, 57)) + +const fn1 = fn.bind; +>fn1 : Symbol(fn1, Decl(instantiatedReferenceMemberLookup.ts, 56, 5)) +>fn.bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>fn : Symbol(fn, Decl(instantiatedReferenceMemberLookup.ts, 55, 13)) +>bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +const fn2 = fn.kind; +>fn2 : Symbol(fn2, Decl(instantiatedReferenceMemberLookup.ts, 57, 5)) +>fn.kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 30), Decl(instantiatedReferenceMemberLookup.ts, 48, 44)) +>fn : Symbol(fn, Decl(instantiatedReferenceMemberLookup.ts, 55, 13)) +>kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 30), Decl(instantiatedReferenceMemberLookup.ts, 48, 44)) + +interface Newable { +>Newable : Symbol(Newable, Decl(instantiatedReferenceMemberLookup.ts, 57, 20)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 59, 18)) + + new (): T; +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 59, 18)) +} +interface NewableBox extends Newable> {} +>NewableBox : Symbol(NewableBox, Decl(instantiatedReferenceMemberLookup.ts, 61, 1)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 62, 21)) +>Newable : Symbol(Newable, Decl(instantiatedReferenceMemberLookup.ts, 57, 20)) +>Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 62, 21)) + +declare const newable: NewableBox; +>newable : Symbol(newable, Decl(instantiatedReferenceMemberLookup.ts, 64, 13)) +>NewableBox : Symbol(NewableBox, Decl(instantiatedReferenceMemberLookup.ts, 61, 1)) + +const newable1 = newable.prototype; +>newable1 : Symbol(newable1, Decl(instantiatedReferenceMemberLookup.ts, 65, 5)) +>newable.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) +>newable : Symbol(newable, Decl(instantiatedReferenceMemberLookup.ts, 64, 13)) +>prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) + +const newable2 = new newable().contents; +>newable2 : Symbol(newable2, Decl(instantiatedReferenceMemberLookup.ts, 66, 5)) +>new newable().contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) +>newable : Symbol(newable, Decl(instantiatedReferenceMemberLookup.ts, 64, 13)) +>contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) + +interface Wrap extends T {} +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceMemberLookup.ts, 66, 40)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 68, 15)) +>a : Symbol(a, Decl(instantiatedReferenceMemberLookup.ts, 68, 26)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 68, 15)) + +declare const wrap: Wrap<{ a: string; b: number }>; +>wrap : Symbol(wrap, Decl(instantiatedReferenceMemberLookup.ts, 70, 13)) +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceMemberLookup.ts, 66, 40)) +>a : Symbol(a, Decl(instantiatedReferenceMemberLookup.ts, 70, 26)) +>b : Symbol(b, Decl(instantiatedReferenceMemberLookup.ts, 70, 37)) + +const wrap1 = wrap.b; +>wrap1 : Symbol(wrap1, Decl(instantiatedReferenceMemberLookup.ts, 71, 5)) +>wrap.b : Symbol(b, Decl(instantiatedReferenceMemberLookup.ts, 70, 37)) +>wrap : Symbol(wrap, Decl(instantiatedReferenceMemberLookup.ts, 70, 13)) +>b : Symbol(b, Decl(instantiatedReferenceMemberLookup.ts, 70, 37)) + +wrap.c; +>wrap : Symbol(wrap, Decl(instantiatedReferenceMemberLookup.ts, 70, 13)) + +interface Tree extends Array> { +>Tree : Symbol(Tree, Decl(instantiatedReferenceMemberLookup.ts, 72, 7)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 74, 15)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Tree : Symbol(Tree, Decl(instantiatedReferenceMemberLookup.ts, 72, 7)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 74, 15)) + + value: T; +>value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) +>T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 74, 15)) +} + +declare const tree: Tree; +>tree : Symbol(tree, Decl(instantiatedReferenceMemberLookup.ts, 78, 13)) +>Tree : Symbol(Tree, Decl(instantiatedReferenceMemberLookup.ts, 72, 7)) + +const tree1 = tree[0].value; +>tree1 : Symbol(tree1, Decl(instantiatedReferenceMemberLookup.ts, 79, 5)) +>tree[0].value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) +>tree : Symbol(tree, Decl(instantiatedReferenceMemberLookup.ts, 78, 13)) +>value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) + +const tree2 = tree.map(child => child.value); +>tree2 : Symbol(tree2, Decl(instantiatedReferenceMemberLookup.ts, 80, 5)) +>tree.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>tree : Symbol(tree, Decl(instantiatedReferenceMemberLookup.ts, 78, 13)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>child : Symbol(child, Decl(instantiatedReferenceMemberLookup.ts, 80, 23)) +>child.value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) +>child : Symbol(child, Decl(instantiatedReferenceMemberLookup.ts, 80, 23)) +>value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) + diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types new file mode 100644 index 0000000000000..46483d5c30262 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types @@ -0,0 +1,245 @@ +//// [tests/cases/compiler/instantiatedReferenceMemberLookup.ts] //// + +=== instantiatedReferenceMemberLookup.ts === +// Property lookups on instantiated class and interface references: declared +// and inherited members, members hidden by redeclarations, accessibility, +// `this` types, Object and Function members, and base types that change shape +// when instantiated. + +interface Base { + value: T; +>value : T + + method(): T; +>method : () => T + + shared: string; +>shared : string +} + +interface Derived extends Base { + shared: "derived"; +>shared : "derived" + + own: T; +>own : T +} + +declare const d: Derived; +>d : Derived + +const d1 = d.value; +>d1 : number[] +>d.value : number[] +>d : Derived +>value : number[] + +const d2 = d.method(); +>d2 : number[] +>d.method() : number[] +>d.method : () => number[] +>d : Derived +>method : () => number[] + +const d3 = d.shared; +>d3 : "derived" +>d.shared : "derived" +>d : Derived +>shared : "derived" + +const d4 = d.own; +>d4 : number +>d.own : number +>d : Derived +>own : number + +const d5 = d.toString(); +>d5 : string +>d.toString() : string +>d.toString : () => string +>d : Derived +>toString : () => string + +d.missing; +>d.missing : any +>d : Derived +>missing : any + +class Box { +>Box : Box + + private secret!: T; +>secret : T + + protected guarded!: T; +>guarded : T + + contents!: T; +>contents : T + + self(): this { +>self : () => this + + return this; +>this : this + } +} + +class NumberBox extends Box { +>NumberBox : NumberBox +>Box : Box + + read() { +>read : () => number + + return this.guarded; +>this.guarded : number +>this : this +>guarded : number + } +} + +declare const box: Box; +>box : Box + +const box1 = box.contents; +>box1 : string +>box.contents : string +>box : Box +>contents : string + +const box2 = box.self().contents; +>box2 : string +>box.self().contents : string +>box.self() : Box +>box.self : () => Box +>box : Box +>self : () => Box +>contents : string + +box.secret; +>box.secret : string +>box : Box +>secret : string + +box.guarded; +>box.guarded : string +>box : Box +>guarded : string + +declare const numberBox: NumberBox; +>numberBox : NumberBox + +const numberBox1 = numberBox.self().read(); +>numberBox1 : number +>numberBox.self().read() : number +>numberBox.self().read : () => number +>numberBox.self() : NumberBox +>numberBox.self : () => NumberBox +>numberBox : NumberBox +>self : () => NumberBox +>read : () => number + +type Callable = { (): void; kind: T } & { kind: "a" }; +>Callable : Callable +>kind : T +>kind : "a" + +interface Fn extends Callable {} + +declare const neverFn: Fn<"b">; +>neverFn : Fn<"b"> + +neverFn.bind; +>neverFn.bind : any +>neverFn : Fn<"b"> +>bind : any + +neverFn.kind; +>neverFn.kind : any +>neverFn : Fn<"b"> +>kind : any + +declare const fn: Fn<"a">; +>fn : Fn<"a"> + +const fn1 = fn.bind; +>fn1 : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>fn.bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>fn : Fn<"a"> +>bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } + +const fn2 = fn.kind; +>fn2 : "a" +>fn.kind : "a" +>fn : Fn<"a"> +>kind : "a" + +interface Newable { + new (): T; +} +interface NewableBox extends Newable> {} + +declare const newable: NewableBox; +>newable : NewableBox + +const newable1 = newable.prototype; +>newable1 : any +>newable.prototype : any +>newable : NewableBox +>prototype : any + +const newable2 = new newable().contents; +>newable2 : string +>new newable().contents : string +>new newable() : Box +>newable : NewableBox +>contents : string + +interface Wrap extends T {} +>a : string + +declare const wrap: Wrap<{ a: string; b: number }>; +>wrap : Wrap<{ a: string; b: number; }> +>a : string +>b : number + +const wrap1 = wrap.b; +>wrap1 : number +>wrap.b : number +>wrap : Wrap<{ a: string; b: number; }> +>b : number + +wrap.c; +>wrap.c : any +>wrap : Wrap<{ a: string; b: number; }> +>c : any + +interface Tree extends Array> { + value: T; +>value : T +} + +declare const tree: Tree; +>tree : Tree + +const tree1 = tree[0].value; +>tree1 : string +>tree[0].value : string +>tree[0] : Tree +>tree : Tree +>0 : 0 +>value : string + +const tree2 = tree.map(child => child.value); +>tree2 : string[] +>tree.map(child => child.value) : string[] +>tree.map : (callbackfn: (value: Tree, index: number, array: Tree[]) => U, thisArg?: any) => U[] +>tree : Tree +>map : (callbackfn: (value: Tree, index: number, array: Tree[]) => U, thisArg?: any) => U[] +>child => child.value : (child: Tree) => string +>child : Tree +>child.value : string +>child : Tree +>value : string + diff --git a/tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts b/tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts new file mode 100644 index 0000000000000..8d04bdcf9e124 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts @@ -0,0 +1,85 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// Property lookups on instantiated class and interface references: declared +// and inherited members, members hidden by redeclarations, accessibility, +// `this` types, Object and Function members, and base types that change shape +// when instantiated. + +interface Base { + value: T; + method(): T; + shared: string; +} + +interface Derived extends Base { + shared: "derived"; + own: T; +} + +declare const d: Derived; +const d1 = d.value; +const d2 = d.method(); +const d3 = d.shared; +const d4 = d.own; +const d5 = d.toString(); +d.missing; + +class Box { + private secret!: T; + protected guarded!: T; + contents!: T; + self(): this { + return this; + } +} + +class NumberBox extends Box { + read() { + return this.guarded; + } +} + +declare const box: Box; +const box1 = box.contents; +const box2 = box.self().contents; +box.secret; +box.guarded; + +declare const numberBox: NumberBox; +const numberBox1 = numberBox.self().read(); + +type Callable = { (): void; kind: T } & { kind: "a" }; +interface Fn extends Callable {} + +declare const neverFn: Fn<"b">; +neverFn.bind; +neverFn.kind; + +declare const fn: Fn<"a">; +const fn1 = fn.bind; +const fn2 = fn.kind; + +interface Newable { + new (): T; +} +interface NewableBox extends Newable> {} + +declare const newable: NewableBox; +const newable1 = newable.prototype; +const newable2 = new newable().contents; + +interface Wrap extends T {} + +declare const wrap: Wrap<{ a: string; b: number }>; +const wrap1 = wrap.b; +wrap.c; + +interface Tree extends Array> { + value: T; +} + +declare const tree: Tree; +const tree1 = tree[0].value; +const tree2 = tree.map(child => child.value); From f0359066578b0ce7a8b15d97df4919e4abe15b76 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Sat, 26 Sep 2026 20:24:55 -0400 Subject: [PATCH 2/9] Answer shape queries on instantiated references without resolving members isWeakType, getSingleSignature, getSignaturesOfStructuredType, getIndexInfosOfStructuredType, isEmptyObjectType, isFunctionObjectType and isStringIndexSignatureOnlyType resolved all members of an instantiated class or interface reference just to count its properties, signatures or index infos, or to see whether its properties are optional. For a reference with a lazy member table these are now answered from the table. Signatures are counted from the instantiated declared signatures and those of the prepared base types. Whether there are properties, and whether all of them are optional, follows from the declared members (which have the same flags as their instantiations) and the properties of the base types, merged as in addInheritedMembers. Index infos are merged from the instantiated declared index infos and those of the base types, as in resolveObjectTypeMembers. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 29 +++ tsc/internal/checker/lazymembers.go | 92 +++++++- tsc/internal/checker/relater.go | 3 + ...stantiatedReferenceShapeQueries.errors.txt | 109 +++++++++ .../instantiatedReferenceShapeQueries.symbols | 215 ++++++++++++++++++ .../instantiatedReferenceShapeQueries.types | 157 +++++++++++++ .../instantiatedReferenceShapeQueries.ts | 71 ++++++ 7 files changed, 674 insertions(+), 2 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types create mode 100644 tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index ca0379b1984d3..c301e3db80241 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19329,6 +19329,9 @@ func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []* if t.flags&TypeFlagsStructuredType == 0 { return nil } + if shape := c.getLazyShape(t); shape != nil && shape.callSignatureCount+shape.constructSignatureCount == 0 { + return nil + } resolved := c.resolveStructuredTypeMembers(t) if kind == SignatureKindCall { return resolved.signatures[:resolved.callSignatureCount] @@ -19342,6 +19345,9 @@ func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { if t.flags&TypeFlagsStructuredType != 0 { + if indexInfos, ok := c.getIndexInfosLazily(t); ok { + return indexInfos + } return c.resolveStructuredTypeMembers(t).indexInfos } return nil @@ -19716,6 +19722,15 @@ func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature { func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature { if t.flags&TypeFlagsObject != 0 { + if shape := c.getLazyShape(t); shape != nil { + if !allowMembers && (shape.hasProperties || len(c.getIndexInfosOfStructuredType(t)) != 0) { + return nil + } + calls, constructs := shape.callSignatureCount, shape.constructSignatureCount + if !(kind == SignatureKindCall && calls == 1 && constructs == 0 || kind == SignatureKindConstruct && constructs == 1 && calls == 0) { + return nil + } + } resolved := c.resolveStructuredTypeMembers(t) if allowMembers || len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { if kind == SignatureKindCall && len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 { @@ -26933,6 +26948,9 @@ func (c *Checker) isEmptyResolvedType(t *StructuredType) bool { func (c *Checker) isEmptyObjectType(t *Type) bool { switch { case t.flags&TypeFlagsObject != 0: + if shape := c.getLazyShape(t); shape != nil { + return !shape.hasProperties && shape.callSignatureCount+shape.constructSignatureCount == 0 && len(c.getIndexInfosOfStructuredType(t)) == 0 + } return !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) case t.flags&TypeFlagsNonPrimitive != 0: return true @@ -27811,6 +27829,9 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node } func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { + if shape := c.getLazyShape(t); shape != nil && (shape.hasProperties || len(c.getIndexInfosOfStructuredType(t)) == 0) { + return false + } return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType) } @@ -31591,6 +31612,14 @@ func (c *Checker) isFunctionObjectType(t *Type) bool { } // We do a quick check for a "bind" property before performing the more expensive subtype // check. This gives us a quicker out in the common case where an object type is not a function. + if shape := c.getLazyShape(t); shape != nil { + if shape.callSignatureCount+shape.constructSignatureCount != 0 { + return true + } + if bind, ok := c.lookupMemberLazily(t, "bind"); ok && bind == nil { + return false + } + } resolved := c.resolveStructuredTypeMembers(t) return len(resolved.signatures) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) } diff --git a/tsc/internal/checker/lazymembers.go b/tsc/internal/checker/lazymembers.go index dfb3b1bcfb29c..728acbbd4937e 100644 --- a/tsc/internal/checker/lazymembers.go +++ b/tsc/internal/checker/lazymembers.go @@ -4,6 +4,7 @@ import ( "slices" "github.com/microsoft/TypeScript/tsc/internal/ast" + "github.com/microsoft/TypeScript/tsc/internal/collections" "github.com/microsoft/TypeScript/tsc/internal/core" ) @@ -46,6 +47,8 @@ type lazyMemberTable struct { found map[string]*ast.Symbol // memoized lookups; nil means no such member resolving *resolvingMembers shape *lazyShape + allIndexInfos []*IndexInfo // declared and inherited index infos, once computed + allIndexInfosDone bool } // lazyShape describes the members of a reference whose lazy member table is @@ -53,6 +56,9 @@ type lazyMemberTable struct { type lazyShape struct { callSignatureCount int constructSignatureCount int + hasProperties bool + optionalityDone bool + onlyOptionalProperties bool } // resolvingMembers holds the members of a lazily prepared type that is being @@ -302,6 +308,33 @@ func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, return result, true } +// getIndexInfosLazily returns resolveStructuredTypeMembers(t).indexInfos +// without resolving t's members (e.g. for `arr[i]` on an Array). +func (c *Checker) getIndexInfosLazily(t *Type) ([]*IndexInfo, bool) { + lm := c.getLazyMemberTable(t) + if lm == nil || lm.state != lazyMembersReady { + return nil, false + } + if !lm.allIndexInfosDone { + // Mirrors resolveObjectTypeMembers. + indexInfos := lm.indexInfos + for _, baseType := range lm.baseTypes { + var inheritedIndexInfos []*IndexInfo + if baseType != c.anyType { + inheritedIndexInfos = c.getIndexInfosOfType(baseType) + } else { + inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} + } + indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { + return findIndexInfo(indexInfos, info.keyType) == nil + })) + } + lm.allIndexInfos = indexInfos + lm.allIndexInfosDone = true + } + return lm.allIndexInfos, true +} + // getNamedPropertyOfType finds name among getPropertiesOfType(t). func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { reduced := c.getReducedApparentType(t) @@ -323,8 +356,9 @@ func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { } // getLazyShape returns the shape of t when t has a lazy member table that is -// ready. It is computed from the declared signatures and those of the -// prepared base types, which resolveObjectTypeMembers concatenates. +// ready. It is computed from the declared members and signatures and those +// of the prepared base types, which resolveObjectTypeMembers concatenates +// (signatures) or merges (properties). func (c *Checker) getLazyShape(t *Type) *lazyShape { lm := c.getLazyMemberTable(t) if lm == nil || lm.state != lazyMembersReady { @@ -335,16 +369,70 @@ func (c *Checker) getLazyShape(t *Type) *lazyShape { callSignatureCount: len(lm.callSignatures), constructSignatureCount: len(lm.constructSignatures), } + for id, symbol := range c.resolveDeclaredMembers(t.Target()).declaredMembers { + if c.isNamedMember(symbol, id) { + shape.hasProperties = true + break + } + } for _, baseType := range lm.baseTypes { if baseShape := c.getLazyShape(c.getReducedApparentType(baseType)); baseShape != nil { shape.callSignatureCount += baseShape.callSignatureCount shape.constructSignatureCount += baseShape.constructSignatureCount + shape.hasProperties = shape.hasProperties || baseShape.hasProperties } else { shape.callSignatureCount += len(c.getSignaturesOfType(baseType, SignatureKindCall)) shape.constructSignatureCount += len(c.getSignaturesOfType(baseType, SignatureKindConstruct)) + shape.hasProperties = shape.hasProperties || core.Some(c.getPropertiesOfType(baseType), isInheritableProperty) } } lm.shape = shape } return lm.shape } + +func isInheritableProperty(prop *ast.Symbol) bool { + return !isStaticPrivateIdentifierProperty(prop) +} + +// hasOnlyOptionalLazyProperties reports whether every property of t, which +// has a lazy member table that is ready, is optional. +func (c *Checker) hasOnlyOptionalLazyProperties(t *Type) bool { + shape := c.getLazyShape(t) + if !shape.optionalityDone { + var seen collections.Set[string] + shape.onlyOptionalProperties = c.everyLazyProperty(t, &seen, func(prop *ast.Symbol) bool { + return prop.Flags&ast.SymbolFlagsOptional != 0 + }) + shape.optionalityDone = true + } + return shape.onlyOptionalProperties +} + +// everyLazyProperty reports whether f holds for every property of t, which has +// a lazy member table that is ready. As in addInheritedMembers, a property +// hides inherited properties of the same name; seen tracks those names. A +// declared member is passed as declared, which has the same flags as its +// instantiation. +func (c *Checker) everyLazyProperty(t *Type, seen *collections.Set[string], f func(prop *ast.Symbol) bool) bool { + for id, symbol := range c.resolveDeclaredMembers(t.Target()).declaredMembers { + if c.isNamedMember(symbol, id) && seen.AddIfAbsent(id) && !f(symbol) { + return false + } + } + for _, baseType := range c.lazyMemberTables[t].baseTypes { + reduced := c.getReducedApparentType(baseType) + if c.getLazyShape(reduced) != nil { + if !c.everyLazyProperty(reduced, seen, f) { + return false + } + continue + } + for _, prop := range c.getPropertiesOfType(baseType) { + if isInheritableProperty(prop) && seen.AddIfAbsent(prop.Name) && !f(prop) { + return false + } + } + } + return true +} diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index 9d71d2504ca0c..194cf2e00a820 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -678,6 +678,9 @@ func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *T // and no required properties, call/construct signatures or index signatures func (c *Checker) isWeakType(t *Type) bool { if t.flags&TypeFlagsObject != 0 { + if shape := c.getLazyShape(t); shape != nil { + return shape.callSignatureCount+shape.constructSignatureCount == 0 && len(c.getIndexInfosOfStructuredType(t)) == 0 && shape.hasProperties && c.hasOnlyOptionalLazyProperties(t) + } resolved := c.resolveStructuredTypeMembers(t) return len(resolved.signatures) == 0 && len(resolved.indexInfos) == 0 && len(resolved.properties) > 0 && core.Every(resolved.properties, func(p *ast.Symbol) bool { return p.Flags&ast.SymbolFlagsOptional != 0 diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt new file mode 100644 index 0000000000000..d94bb38443cfb --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt @@ -0,0 +1,109 @@ +instantiatedReferenceShapeQueries.ts(12,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<"b">'. +instantiatedReferenceShapeQueries.ts(13,7): error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'Weak<"a">'. +instantiatedReferenceShapeQueries.ts(22,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptional'. +instantiatedReferenceShapeQueries.ts(23,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptionalDerived'. +instantiatedReferenceShapeQueries.ts(27,41): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type 'Empty<"a">'. +instantiatedReferenceShapeQueries.ts(35,7): error TS2322: Type '(x: U, y: string) => U' is not assignable to type 'number'. +instantiatedReferenceShapeQueries.ts(38,7): error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'. +instantiatedReferenceShapeQueries.ts(46,7): error TS2322: Type 'Dict<"b">' is not assignable to type '{ [key: string]: number; }'. + 'string' index signatures are incompatible. + Type 'unknown' is not assignable to type 'number'. +instantiatedReferenceShapeQueries.ts(48,11): error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. + 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'. +instantiatedReferenceShapeQueries.ts(50,7): error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'. + + +==== instantiatedReferenceShapeQueries.ts (10 errors) ==== + // Shape queries (weak types, single signatures, empty object types, string + // index signature only types, function object types) on instantiated interface + // references, including ones whose base types change shape when instantiated. + + type Tagged = { kind: T } & { kind: "a" }; + + declare const other: { other: number }; + + interface Weak extends Tagged { + opt?: number; + } + const weak1: Weak<"b"> = other; + ~~~~~ +!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<"b">'. + const weak2: Weak<"a"> = other; + ~~~~~ +!!! error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'Weak<"a">'. +!!! related TS2728 instantiatedReferenceShapeQueries.ts:5:20: 'kind' is declared here. + + interface AllOptional { + a?: T; + b?: T[]; + } + interface AllOptionalDerived extends AllOptional { + c?: T; + } + const weak3: AllOptional = other; + ~~~~~ +!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptional'. + const weak4: AllOptionalDerived = other; + ~~~~~ +!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptionalDerived'. + + interface Empty extends Tagged {} + const empty1: Empty<"b"> = { extra: 1 }; + const empty2: Empty<"a"> = { kind: "a", extra: 1 }; + ~~~~~ +!!! error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type 'Empty<"a">'. + + type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; + interface Overloads extends Two {} + + declare function pipe(f: (...args: A) => B): (...args: A) => B; + + declare const merged: Overloads; + const piped1: number = pipe(merged); + ~~~~~~ +!!! error TS2322: Type '(x: U, y: string) => U' is not assignable to type 'number'. + + declare const distinct: Overloads; + const piped2: number = pipe(distinct); + ~~~~~~ +!!! error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'. + + interface Dict extends Tagged { + [key: string]: unknown; + } + + declare const dict: Dict<"b">; + const dict1 = dict["anything"]; + const dict2: { [key: string]: number } = dict; + ~~~~~ +!!! error TS2322: Type 'Dict<"b">' is not assignable to type '{ [key: string]: number; }'. +!!! error TS2322: 'string' index signatures are incompatible. +!!! error TS2322: Type 'unknown' is not assignable to type 'number'. + + interface Wrap extends T {} + ~~~~ +!!! error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. +!!! error TS2430: 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'. + + const wrap1: Wrap<{ a?: string; b: number }> = other; + ~~~~~ +!!! error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'. +!!! related TS2728 instantiatedReferenceShapeQueries.ts:50:33: 'b' is declared here. + declare const wrap2: Wrap<{ a?: string; (): void }>; + wrap2.bind; + + interface CallableBox { + (): T; + } + interface PlainBox { + value: T; + } + + declare const box: CallableBox | PlainBox; + if (typeof box === "function") { + box; + } + else { + box; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols new file mode 100644 index 0000000000000..d82112660bbc1 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols @@ -0,0 +1,215 @@ +//// [tests/cases/compiler/instantiatedReferenceShapeQueries.ts] //// + +=== instantiatedReferenceShapeQueries.ts === +// Shape queries (weak types, single signatures, empty object types, string +// index signature only types, function object types) on instantiated interface +// references, including ones whose base types change shape when instantiated. + +type Tagged = { kind: T } & { kind: "a" }; +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 4, 12)) +>kind : Symbol(kind, Decl(instantiatedReferenceShapeQueries.ts, 4, 18)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 4, 12)) +>kind : Symbol(kind, Decl(instantiatedReferenceShapeQueries.ts, 4, 32)) + +declare const other: { other: number }; +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 22)) + +interface Weak extends Tagged { +>Weak : Symbol(Weak, Decl(instantiatedReferenceShapeQueries.ts, 6, 39)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 8, 15)) +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 8, 15)) + + opt?: number; +>opt : Symbol(Weak.opt, Decl(instantiatedReferenceShapeQueries.ts, 8, 52)) +} +const weak1: Weak<"b"> = other; +>weak1 : Symbol(weak1, Decl(instantiatedReferenceShapeQueries.ts, 11, 5)) +>Weak : Symbol(Weak, Decl(instantiatedReferenceShapeQueries.ts, 6, 39)) +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) + +const weak2: Weak<"a"> = other; +>weak2 : Symbol(weak2, Decl(instantiatedReferenceShapeQueries.ts, 12, 5)) +>Weak : Symbol(Weak, Decl(instantiatedReferenceShapeQueries.ts, 6, 39)) +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) + +interface AllOptional { +>AllOptional : Symbol(AllOptional, Decl(instantiatedReferenceShapeQueries.ts, 12, 31)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 14, 22)) + + a?: T; +>a : Symbol(AllOptional.a, Decl(instantiatedReferenceShapeQueries.ts, 14, 26)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 14, 22)) + + b?: T[]; +>b : Symbol(AllOptional.b, Decl(instantiatedReferenceShapeQueries.ts, 15, 10)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 14, 22)) +} +interface AllOptionalDerived extends AllOptional { +>AllOptionalDerived : Symbol(AllOptionalDerived, Decl(instantiatedReferenceShapeQueries.ts, 17, 1)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 18, 29)) +>AllOptional : Symbol(AllOptional, Decl(instantiatedReferenceShapeQueries.ts, 12, 31)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 18, 29)) + + c?: T; +>c : Symbol(AllOptionalDerived.c, Decl(instantiatedReferenceShapeQueries.ts, 18, 56)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 18, 29)) +} +const weak3: AllOptional = other; +>weak3 : Symbol(weak3, Decl(instantiatedReferenceShapeQueries.ts, 21, 5)) +>AllOptional : Symbol(AllOptional, Decl(instantiatedReferenceShapeQueries.ts, 12, 31)) +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) + +const weak4: AllOptionalDerived = other; +>weak4 : Symbol(weak4, Decl(instantiatedReferenceShapeQueries.ts, 22, 5)) +>AllOptionalDerived : Symbol(AllOptionalDerived, Decl(instantiatedReferenceShapeQueries.ts, 17, 1)) +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) + +interface Empty extends Tagged {} +>Empty : Symbol(Empty, Decl(instantiatedReferenceShapeQueries.ts, 22, 48)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 24, 16)) +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 24, 16)) + +const empty1: Empty<"b"> = { extra: 1 }; +>empty1 : Symbol(empty1, Decl(instantiatedReferenceShapeQueries.ts, 25, 5)) +>Empty : Symbol(Empty, Decl(instantiatedReferenceShapeQueries.ts, 22, 48)) +>extra : Symbol(extra, Decl(instantiatedReferenceShapeQueries.ts, 25, 28)) + +const empty2: Empty<"a"> = { kind: "a", extra: 1 }; +>empty2 : Symbol(empty2, Decl(instantiatedReferenceShapeQueries.ts, 26, 5)) +>Empty : Symbol(Empty, Decl(instantiatedReferenceShapeQueries.ts, 22, 48)) +>kind : Symbol(kind, Decl(instantiatedReferenceShapeQueries.ts, 26, 28)) +>extra : Symbol(extra, Decl(instantiatedReferenceShapeQueries.ts, 26, 39)) + +type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; +>Two : Symbol(Two, Decl(instantiatedReferenceShapeQueries.ts, 26, 51)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 28, 9)) +>U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 17)) +>x : Symbol(x, Decl(instantiatedReferenceShapeQueries.ts, 28, 20)) +>U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 17)) +>y : Symbol(y, Decl(instantiatedReferenceShapeQueries.ts, 28, 25)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 28, 9)) +>U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 17)) +>U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 42)) +>x : Symbol(x, Decl(instantiatedReferenceShapeQueries.ts, 28, 45)) +>U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 42)) +>y : Symbol(y, Decl(instantiatedReferenceShapeQueries.ts, 28, 50)) +>U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 42)) + +interface Overloads extends Two {} +>Overloads : Symbol(Overloads, Decl(instantiatedReferenceShapeQueries.ts, 28, 67)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 29, 20)) +>Two : Symbol(Two, Decl(instantiatedReferenceShapeQueries.ts, 26, 51)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 29, 20)) + +declare function pipe(f: (...args: A) => B): (...args: A) => B; +>pipe : Symbol(pipe, Decl(instantiatedReferenceShapeQueries.ts, 29, 40)) +>A : Symbol(A, Decl(instantiatedReferenceShapeQueries.ts, 31, 22)) +>B : Symbol(B, Decl(instantiatedReferenceShapeQueries.ts, 31, 38)) +>f : Symbol(f, Decl(instantiatedReferenceShapeQueries.ts, 31, 42)) +>args : Symbol(args, Decl(instantiatedReferenceShapeQueries.ts, 31, 46)) +>A : Symbol(A, Decl(instantiatedReferenceShapeQueries.ts, 31, 22)) +>B : Symbol(B, Decl(instantiatedReferenceShapeQueries.ts, 31, 38)) +>args : Symbol(args, Decl(instantiatedReferenceShapeQueries.ts, 31, 66)) +>A : Symbol(A, Decl(instantiatedReferenceShapeQueries.ts, 31, 22)) +>B : Symbol(B, Decl(instantiatedReferenceShapeQueries.ts, 31, 38)) + +declare const merged: Overloads; +>merged : Symbol(merged, Decl(instantiatedReferenceShapeQueries.ts, 33, 13)) +>Overloads : Symbol(Overloads, Decl(instantiatedReferenceShapeQueries.ts, 28, 67)) + +const piped1: number = pipe(merged); +>piped1 : Symbol(piped1, Decl(instantiatedReferenceShapeQueries.ts, 34, 5)) +>pipe : Symbol(pipe, Decl(instantiatedReferenceShapeQueries.ts, 29, 40)) +>merged : Symbol(merged, Decl(instantiatedReferenceShapeQueries.ts, 33, 13)) + +declare const distinct: Overloads; +>distinct : Symbol(distinct, Decl(instantiatedReferenceShapeQueries.ts, 36, 13)) +>Overloads : Symbol(Overloads, Decl(instantiatedReferenceShapeQueries.ts, 28, 67)) + +const piped2: number = pipe(distinct); +>piped2 : Symbol(piped2, Decl(instantiatedReferenceShapeQueries.ts, 37, 5)) +>pipe : Symbol(pipe, Decl(instantiatedReferenceShapeQueries.ts, 29, 40)) +>distinct : Symbol(distinct, Decl(instantiatedReferenceShapeQueries.ts, 36, 13)) + +interface Dict extends Tagged { +>Dict : Symbol(Dict, Decl(instantiatedReferenceShapeQueries.ts, 37, 38)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 39, 15)) +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 39, 15)) + + [key: string]: unknown; +>key : Symbol(key, Decl(instantiatedReferenceShapeQueries.ts, 40, 5)) +} + +declare const dict: Dict<"b">; +>dict : Symbol(dict, Decl(instantiatedReferenceShapeQueries.ts, 43, 13)) +>Dict : Symbol(Dict, Decl(instantiatedReferenceShapeQueries.ts, 37, 38)) + +const dict1 = dict["anything"]; +>dict1 : Symbol(dict1, Decl(instantiatedReferenceShapeQueries.ts, 44, 5)) +>dict : Symbol(dict, Decl(instantiatedReferenceShapeQueries.ts, 43, 13)) + +const dict2: { [key: string]: number } = dict; +>dict2 : Symbol(dict2, Decl(instantiatedReferenceShapeQueries.ts, 45, 5)) +>key : Symbol(key, Decl(instantiatedReferenceShapeQueries.ts, 45, 16)) +>dict : Symbol(dict, Decl(instantiatedReferenceShapeQueries.ts, 43, 13)) + +interface Wrap extends T {} +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceShapeQueries.ts, 45, 46)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 47, 15)) +>a : Symbol(a, Decl(instantiatedReferenceShapeQueries.ts, 47, 26)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 47, 15)) + +const wrap1: Wrap<{ a?: string; b: number }> = other; +>wrap1 : Symbol(wrap1, Decl(instantiatedReferenceShapeQueries.ts, 49, 5)) +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceShapeQueries.ts, 45, 46)) +>a : Symbol(a, Decl(instantiatedReferenceShapeQueries.ts, 49, 19)) +>b : Symbol(b, Decl(instantiatedReferenceShapeQueries.ts, 49, 31)) +>other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) + +declare const wrap2: Wrap<{ a?: string; (): void }>; +>wrap2 : Symbol(wrap2, Decl(instantiatedReferenceShapeQueries.ts, 50, 13)) +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceShapeQueries.ts, 45, 46)) +>a : Symbol(a, Decl(instantiatedReferenceShapeQueries.ts, 50, 27)) + +wrap2.bind; +>wrap2.bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>wrap2 : Symbol(wrap2, Decl(instantiatedReferenceShapeQueries.ts, 50, 13)) +>bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +interface CallableBox { +>CallableBox : Symbol(CallableBox, Decl(instantiatedReferenceShapeQueries.ts, 51, 11)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 53, 22)) + + (): T; +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 53, 22)) +} +interface PlainBox { +>PlainBox : Symbol(PlainBox, Decl(instantiatedReferenceShapeQueries.ts, 55, 1)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 56, 19)) + + value: T; +>value : Symbol(PlainBox.value, Decl(instantiatedReferenceShapeQueries.ts, 56, 23)) +>T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 56, 19)) +} + +declare const box: CallableBox | PlainBox; +>box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) +>CallableBox : Symbol(CallableBox, Decl(instantiatedReferenceShapeQueries.ts, 51, 11)) +>PlainBox : Symbol(PlainBox, Decl(instantiatedReferenceShapeQueries.ts, 55, 1)) + +if (typeof box === "function") { +>box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) + + box; +>box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) +} +else { + box; +>box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types new file mode 100644 index 0000000000000..cc797f4c91075 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types @@ -0,0 +1,157 @@ +//// [tests/cases/compiler/instantiatedReferenceShapeQueries.ts] //// + +=== instantiatedReferenceShapeQueries.ts === +// Shape queries (weak types, single signatures, empty object types, string +// index signature only types, function object types) on instantiated interface +// references, including ones whose base types change shape when instantiated. + +type Tagged = { kind: T } & { kind: "a" }; +>Tagged : Tagged +>kind : T +>kind : "a" + +declare const other: { other: number }; +>other : { other: number; } +>other : number + +interface Weak extends Tagged { + opt?: number; +>opt : number | undefined +} +const weak1: Weak<"b"> = other; +>weak1 : Weak<"b"> +>other : { other: number; } + +const weak2: Weak<"a"> = other; +>weak2 : Weak<"a"> +>other : { other: number; } + +interface AllOptional { + a?: T; +>a : T | undefined + + b?: T[]; +>b : T[] | undefined +} +interface AllOptionalDerived extends AllOptional { + c?: T; +>c : T | undefined +} +const weak3: AllOptional = other; +>weak3 : AllOptional +>other : { other: number; } + +const weak4: AllOptionalDerived = other; +>weak4 : AllOptionalDerived +>other : { other: number; } + +interface Empty extends Tagged {} +const empty1: Empty<"b"> = { extra: 1 }; +>empty1 : Empty<"b"> +>{ extra: 1 } : { extra: number; } +>extra : number +>1 : 1 + +const empty2: Empty<"a"> = { kind: "a", extra: 1 }; +>empty2 : Empty<"a"> +>{ kind: "a", extra: 1 } : { kind: "a"; extra: number; } +>kind : "a" +>"a" : "a" +>extra : number +>1 : 1 + +type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; +>Two : Two +>x : U +>y : T +>x : U +>y : string + +interface Overloads extends Two {} + +declare function pipe(f: (...args: A) => B): (...args: A) => B; +>pipe : (f: (...args: A) => B) => (...args: A) => B +>f : (...args: A) => B +>args : A +>args : A + +declare const merged: Overloads; +>merged : Overloads + +const piped1: number = pipe(merged); +>piped1 : number +>pipe(merged) : (x: U, y: string) => U +>pipe : (f: (...args: A) => B) => (...args: A) => B +>merged : Overloads + +declare const distinct: Overloads; +>distinct : Overloads + +const piped2: number = pipe(distinct); +>piped2 : number +>pipe(distinct) : (x: unknown, y: string) => unknown +>pipe : (f: (...args: A) => B) => (...args: A) => B +>distinct : Overloads + +interface Dict extends Tagged { + [key: string]: unknown; +>key : string +} + +declare const dict: Dict<"b">; +>dict : Dict<"b"> + +const dict1 = dict["anything"]; +>dict1 : unknown +>dict["anything"] : unknown +>dict : Dict<"b"> +>"anything" : "anything" + +const dict2: { [key: string]: number } = dict; +>dict2 : { [key: string]: number; } +>key : string +>dict : Dict<"b"> + +interface Wrap extends T {} +>a : string | undefined + +const wrap1: Wrap<{ a?: string; b: number }> = other; +>wrap1 : Wrap<{ a?: string; b: number; }> +>a : string | undefined +>b : number +>other : { other: number; } + +declare const wrap2: Wrap<{ a?: string; (): void }>; +>wrap2 : Wrap<{ (): void; a?: string; }> +>a : string | undefined + +wrap2.bind; +>wrap2.bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>wrap2 : Wrap<{ (): void; a?: string; }> +>bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } + +interface CallableBox { + (): T; +} +interface PlainBox { + value: T; +>value : T +} + +declare const box: CallableBox | PlainBox; +>box : CallableBox | PlainBox + +if (typeof box === "function") { +>typeof box === "function" : boolean +>typeof box : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>box : CallableBox | PlainBox +>"function" : "function" + + box; +>box : CallableBox +} +else { + box; +>box : PlainBox +} + diff --git a/tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts b/tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts new file mode 100644 index 0000000000000..20c1b8ad4ed42 --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts @@ -0,0 +1,71 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// Shape queries (weak types, single signatures, empty object types, string +// index signature only types, function object types) on instantiated interface +// references, including ones whose base types change shape when instantiated. + +type Tagged = { kind: T } & { kind: "a" }; + +declare const other: { other: number }; + +interface Weak extends Tagged { + opt?: number; +} +const weak1: Weak<"b"> = other; +const weak2: Weak<"a"> = other; + +interface AllOptional { + a?: T; + b?: T[]; +} +interface AllOptionalDerived extends AllOptional { + c?: T; +} +const weak3: AllOptional = other; +const weak4: AllOptionalDerived = other; + +interface Empty extends Tagged {} +const empty1: Empty<"b"> = { extra: 1 }; +const empty2: Empty<"a"> = { kind: "a", extra: 1 }; + +type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; +interface Overloads extends Two {} + +declare function pipe(f: (...args: A) => B): (...args: A) => B; + +declare const merged: Overloads; +const piped1: number = pipe(merged); + +declare const distinct: Overloads; +const piped2: number = pipe(distinct); + +interface Dict extends Tagged { + [key: string]: unknown; +} + +declare const dict: Dict<"b">; +const dict1 = dict["anything"]; +const dict2: { [key: string]: number } = dict; + +interface Wrap extends T {} + +const wrap1: Wrap<{ a?: string; b: number }> = other; +declare const wrap2: Wrap<{ a?: string; (): void }>; +wrap2.bind; + +interface CallableBox { + (): T; +} +interface PlainBox { + value: T; +} + +declare const box: CallableBox | PlainBox; +if (typeof box === "function") { + box; +} +else { + box; +} From 2efc063875b172def3c896c0920c7705cb8d78d2 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Sun, 27 Sep 2026 00:10:25 -0400 Subject: [PATCH 3/9] Don't record members that are about to be resolved in full Resolving the members of a lazily prepared reference in full stored every instantiated member in the table's memo, which is discarded right after, and checked each member against the list of members that instantiate to themselves with a linear scan. The list is now sorted and binary searched, and members are only memoized when looked up individually. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/lazymembers.go | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/tsc/internal/checker/lazymembers.go b/tsc/internal/checker/lazymembers.go index 728acbbd4937e..5067aaba75e06 100644 --- a/tsc/internal/checker/lazymembers.go +++ b/tsc/internal/checker/lazymembers.go @@ -37,7 +37,7 @@ type lazyMemberTable struct { state lazyMembersState mapper *TypeMapper typeArguments []*Type - unaffected []string // declared members that instantiate to themselves + unaffected []string // sorted names of declared members that instantiate to themselves callSignatures []*Signature constructSignatures []*Signature indexInfos []*IndexInfo @@ -135,6 +135,7 @@ func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { lm.unaffected = append(lm.unaffected, id) } } + slices.Sort(lm.unaffected) lm.callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, lm.mapper) lm.constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, lm.mapper) lm.indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, lm.mapper) @@ -207,7 +208,7 @@ func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { members = make(ast.SymbolTable, len(resolved.declaredMembers)) for id, symbol := range resolved.declaredMembers { if c.isNamedMember(symbol, id) { - members[id] = c.getLazyDeclaredMember(lm, symbol, id) + members[id] = c.instantiateLazyDeclaredMember(lm, symbol, id) } } } @@ -256,16 +257,25 @@ func (c *Checker) inheritBaseTypeMembers(r *resolvingMembers, baseType *Type) { func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { result := lm.declared[name] if result == nil { - if slices.Contains(lm.unaffected, name) { - result = symbol - } else { - result = c.newInstantiatedSymbol(symbol, lm.mapper) - } + result = c.instantiateLazyDeclaredMember(lm, symbol, name) lm.declared[name] = result } return result } +// instantiateLazyDeclaredMember is getLazyDeclaredMember without memoizing +// the result, for resolving the members in full, after which the table is +// discarded. +func (c *Checker) instantiateLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { + if result := lm.declared[name]; result != nil { + return result + } + if _, unaffected := slices.BinarySearch(lm.unaffected, name); unaffected { + return symbol + } + return c.newInstantiatedSymbol(symbol, lm.mapper) +} + // lookupMemberLazily returns what resolveStructuredTypeMembers(t).members[name] // would hold, without building t's member table. ok is false when the lazy // path can't answer and the caller must resolve members normally. From af2719bab959105372f85771d33d340d91040eda Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Sun, 27 Sep 2026 00:10:25 -0400 Subject: [PATCH 4/9] Check for resolved members before taking the lazy paths Property and index info lookups are hot, and nearly always see types whose members are resolved. They now check that inline before calling into the lazy member table code, which on material-ui's docs project took 2-3% of check time on its own. The lazy part of getPropertyOfTypeEx moves to getPropertyOfObjectTypeLazily. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 40 +++++++--------------- tsc/internal/checker/lazymembers.go | 52 ++++++++++++++++++++++++++++- 2 files changed, 63 insertions(+), 29 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index c301e3db80241..9932ef25bd72b 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19240,30 +19240,10 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr t = c.getReducedApparentType(t) switch { case t.flags&TypeFlagsObject != 0: - if symbol, ok := c.lookupMemberLazily(t, name); ok { - if symbol != nil && c.symbolIsValueEx(symbol, includeTypeOnlyMembers) { + if mayHaveLazyMembers(t) { + if symbol, ok := c.getPropertyOfObjectTypeLazily(t, name, skipObjectFunctionPropertyAugment, includeTypeOnlyMembers); ok { return symbol } - if skipObjectFunctionPropertyAugment { - return nil - } - if symbol == nil { - if shape := c.getLazyShape(t); shape != nil { - var functionType *Type - switch { - case shape.callSignatureCount != 0: - functionType = c.globalCallableFunctionType - case shape.constructSignatureCount != 0: - functionType = c.globalNewableFunctionType - } - if functionType != nil { - if symbol := c.getPropertyOfObjectType(functionType, name); symbol != nil { - return symbol - } - } - return c.getPropertyOfObjectType(c.globalObjectType, name) - } - } } resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] @@ -19345,8 +19325,10 @@ func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { if t.flags&TypeFlagsStructuredType != 0 { - if indexInfos, ok := c.getIndexInfosLazily(t); ok { - return indexInfos + if mayHaveLazyMembers(t) { + if indexInfos, ok := c.getIndexInfosLazily(t); ok { + return indexInfos + } } return c.resolveStructuredTypeMembers(t).indexInfos } @@ -21793,11 +21775,13 @@ func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, in */ func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { - if symbol, ok := c.lookupMemberLazily(t, name); ok { - if symbol != nil && c.symbolIsValue(symbol) { - return symbol + if mayHaveLazyMembers(t) { + if symbol, ok := c.lookupMemberLazily(t, name); ok { + if symbol != nil && c.symbolIsValue(symbol) { + return symbol + } + return nil } - return nil } resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] diff --git a/tsc/internal/checker/lazymembers.go b/tsc/internal/checker/lazymembers.go index 5067aaba75e06..6ebc7b446766c 100644 --- a/tsc/internal/checker/lazymembers.go +++ b/tsc/internal/checker/lazymembers.go @@ -74,7 +74,7 @@ type resolvingMembers struct { // generic class or interface (other than the declaration itself) whose members // haven't been resolved. func isUnresolvedInstantiatedReference(t *Type) bool { - if t.flags&TypeFlagsObject == 0 || t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) != ObjectFlagsReference { + if t.flags&TypeFlagsObject == 0 || !mayHaveLazyMembers(t) { return false } source := t.Target() @@ -84,6 +84,13 @@ func isUnresolvedInstantiatedReference(t *Type) bool { return t.symbol == nil || t.symbol.Flags&ast.SymbolFlagsValueModule == 0 } +// mayHaveLazyMembers is a quick check for isUnresolvedInstantiatedReference +// that callers on hot paths make first, as most types they see have resolved +// members. +func mayHaveLazyMembers(t *Type) bool { + return t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) == ObjectFlagsReference +} + func (c *Checker) getReferenceMemberTypeArguments(t *Type, source *Type) (typeParameters []*Type, typeArguments []*Type) { typeParameters = source.AsInterfaceType().allTypeParameters typeArguments = c.getTypeArguments(t) @@ -318,6 +325,42 @@ func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, return result, true } +// getPropertyOfObjectTypeLazily returns what getPropertyOfTypeEx returns for +// the object type t without resolving its members. ok is false when the lazy +// path can't answer. +func (c *Checker) getPropertyOfObjectTypeLazily(t *Type, name string, skipObjectFunctionPropertyAugment bool, includeTypeOnlyMembers bool) (symbol *ast.Symbol, ok bool) { + symbol, ok = c.lookupMemberLazily(t, name) + if !ok { + return nil, false + } + if symbol != nil && c.symbolIsValueEx(symbol, includeTypeOnlyMembers) { + return symbol, true + } + if skipObjectFunctionPropertyAugment { + return nil, true + } + if symbol != nil { + return nil, false + } + shape := c.getLazyShape(t) + if shape == nil { + return nil, false + } + var functionType *Type + switch { + case shape.callSignatureCount != 0: + functionType = c.globalCallableFunctionType + case shape.constructSignatureCount != 0: + functionType = c.globalNewableFunctionType + } + if functionType != nil { + if symbol := c.getPropertyOfObjectType(functionType, name); symbol != nil { + return symbol, true + } + } + return c.getPropertyOfObjectType(c.globalObjectType, name), true +} + // getIndexInfosLazily returns resolveStructuredTypeMembers(t).indexInfos // without resolving t's members (e.g. for `arr[i]` on an Array). func (c *Checker) getIndexInfosLazily(t *Type) ([]*IndexInfo, bool) { @@ -370,6 +413,13 @@ func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { // of the prepared base types, which resolveObjectTypeMembers concatenates // (signatures) or merges (properties). func (c *Checker) getLazyShape(t *Type) *lazyShape { + if !mayHaveLazyMembers(t) { + return nil + } + return c.getLazyShapeWorker(t) +} + +func (c *Checker) getLazyShapeWorker(t *Type) *lazyShape { lm := c.getLazyMemberTable(t) if lm == nil || lm.state != lazyMembersReady { return nil From cdac4e38c2a9af3c6129a756a9a72c4c526f2238 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Sun, 27 Sep 2026 13:42:44 -0400 Subject: [PATCH 5/9] Read lazy member tables through the regular accessors Each shape check (isWeakType, getSingleSignature, isStringIndexSignatureOnlyType, isFunctionObjectType, getPropertyOfTypeEx) had a second, lazy version of its condition. Now the lazy table stores the reference's full signatures and index infos, inherited through a helper resolveObjectTypeMembers shares, and getSignaturesOfStructuredType and getIndexInfosOfStructuredType answer from it. The checks read through those accessors and getMemberOfStructuredType, hasPropertiesOfStructuredType and everyPropertyOfStructuredType, so each condition is written once. The separate shape summary goes away, as do the hooks in getPropertyOfObjectType and isEmptyObjectType, which saved no memory, and the code moves into checker.go. This also removes a crash path: isWeakType read the table's shape, called getIndexInfosOfStructuredType, and read the shape again, which would find no table if that call had resolved the type in full. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 475 ++++++++++++++++++++++---- tsc/internal/checker/lazymembers.go | 498 ---------------------------- tsc/internal/checker/relater.go | 7 +- 3 files changed, 411 insertions(+), 569 deletions(-) delete mode 100644 tsc/internal/checker/lazymembers.go diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 9932ef25bd72b..ba08c5fd1ad68 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19240,13 +19240,7 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr t = c.getReducedApparentType(t) switch { case t.flags&TypeFlagsObject != 0: - if mayHaveLazyMembers(t) { - if symbol, ok := c.getPropertyOfObjectTypeLazily(t, name, skipObjectFunctionPropertyAugment, includeTypeOnlyMembers); ok { - return symbol - } - } - resolved := c.resolveStructuredTypeMembers(t) - symbol := resolved.members[name] + symbol := c.getMemberOfStructuredType(t, name) if symbol != nil { if !includeTypeOnlyMembers && t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsValueModule != 0 && c.moduleSymbolLinks.Get(t.symbol).typeOnlyExportStarMap[name] != nil { // If this is the type of a module, `resolved.members.get(name)` might have effectively skipped over @@ -19265,9 +19259,9 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr switch { case t == c.anyFunctionType: functionType = c.globalFunctionType - case len(resolved.CallSignatures()) != 0: + case len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) != 0: functionType = c.globalCallableFunctionType - case len(resolved.ConstructSignatures()) != 0: + case len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) != 0: functionType = c.globalNewableFunctionType } if functionType != nil { @@ -19309,8 +19303,11 @@ func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []* if t.flags&TypeFlagsStructuredType == 0 { return nil } - if shape := c.getLazyShape(t); shape != nil && shape.callSignatureCount+shape.constructSignatureCount == 0 { - return nil + if lm := c.getReadyLazyMemberTable(t); lm != nil { + if kind == SignatureKindCall { + return lm.callSignatures + } + return lm.constructSignatures } resolved := c.resolveStructuredTypeMembers(t) if kind == SignatureKindCall { @@ -19325,10 +19322,8 @@ func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { if t.flags&TypeFlagsStructuredType != 0 { - if mayHaveLazyMembers(t) { - if indexInfos, ok := c.getIndexInfosLazily(t); ok { - return indexInfos - } + if lm := c.getReadyLazyMemberTable(t); lm != nil { + return lm.indexInfos } return c.resolveStructuredTypeMembers(t).indexInfos } @@ -19490,17 +19485,7 @@ func (c *Checker) resolveObjectTypeMembers(t *Type, source *Type, typeParameters instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, mapper), thisArgument, false /*needsApparentType*/) } members = c.addInheritedMembers(members, c.getPropertiesOfType(instantiatedBaseType)) - callSignatures = core.Concatenate(callSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindCall)) - constructSignatures = core.Concatenate(constructSignatures, c.getSignaturesOfType(instantiatedBaseType, SignatureKindConstruct)) - var inheritedIndexInfos []*IndexInfo - if instantiatedBaseType != c.anyType { - inheritedIndexInfos = c.getIndexInfosOfType(instantiatedBaseType) - } else { - inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} - } - indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { - return findIndexInfo(indexInfos, info.keyType) == nil - })) + callSignatures, constructSignatures, indexInfos = c.appendInheritedSignaturesAndIndexInfos(callSignatures, constructSignatures, indexInfos, instantiatedBaseType) } t.objectFlags &^= ObjectFlagsUnresolvedMembers } @@ -19516,6 +19501,394 @@ func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { return nil } +// appendInheritedSignaturesAndIndexInfos appends the signatures and index infos +// a class or interface inherits from baseType. +func (c *Checker) appendInheritedSignaturesAndIndexInfos(callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo, baseType *Type) ([]*Signature, []*Signature, []*IndexInfo) { + callSignatures = core.Concatenate(callSignatures, c.getSignaturesOfType(baseType, SignatureKindCall)) + constructSignatures = core.Concatenate(constructSignatures, c.getSignaturesOfType(baseType, SignatureKindConstruct)) + var inheritedIndexInfos []*IndexInfo + if baseType != c.anyType { + inheritedIndexInfos = c.getIndexInfosOfType(baseType) + } else { + inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} + } + indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { + return findIndexInfo(indexInfos, info.keyType) == nil + })) + return callSignatures, constructSignatures, indexInfos +} + +// Resolving the members of an instantiated class or interface reference +// instantiates every declared member and merges every inherited one, even when +// the checker only needs one of them (`expect(x).toBe`, `arr.map`) or asks +// about the shape of the type (isWeakType, getSingleSignature). In large +// programs most of those symbols are never used. +// +// Such a reference instead gets a lazy member table on first use. Preparing +// the table does what resolveObjectTypeMembers does, in the same order, except +// creating member symbols: signatures and index infos are instantiated and +// inherited as usual, and getSignaturesOfStructuredType and +// getIndexInfosOfStructuredType answer from the table. Member lookups +// instantiate only the requested member, the way resolveObjectTypeMembers +// would have when the table was prepared, and resolving the members in full +// later reuses the symbols already handed out. +// +// While resolveObjectTypeMembers resolves base types, the type exposes its +// declared members (see ObjectFlagsUnresolvedMembers). If preparing the table +// leads back to the type, the type is resolved in full at that point so it +// exposes the same members. + +type lazyMembersState int + +const ( + lazyMembersResolvingDeclared lazyMembersState = iota // instantiating declared signatures and index infos, resolving base types + lazyMembersResolvingBases // instantiating base types and resolving their members + lazyMembersReady + lazyMembersMaterialized // resolved in full while base types were being resolved +) + +type lazyMemberTable struct { + state lazyMembersState + mapper *TypeMapper + typeArguments []*Type + unaffected []string // sorted names of declared members that instantiate to themselves + callSignatures []*Signature // declared, and once the table is ready, inherited + constructSignatures []*Signature + indexInfos []*IndexInfo + baseTypes []*Type // instantiated base types + inheritedCount int // number of base types inherited from so far + declared map[string]*ast.Symbol + found map[string]*ast.Symbol // memoized lookups; nil means no such member + members ast.SymbolTable // once materialized +} + +// isUnresolvedInstantiatedReference reports whether t is a reference to a +// generic class or interface (other than the declaration itself) whose members +// haven't been resolved. +func isUnresolvedInstantiatedReference(t *Type) bool { + if t.flags&TypeFlagsObject == 0 || !mayHaveLazyMembers(t) { + return false + } + source := t.Target() + if source == nil || source == t || source.objectFlags&ObjectFlagsClassOrInterface == 0 || source.objectFlags&ObjectFlagsTuple != 0 { + return false + } + return t.symbol == nil || t.symbol.Flags&ast.SymbolFlagsValueModule == 0 +} + +// mayHaveLazyMembers is a quick check for isUnresolvedInstantiatedReference +// for hot paths, as most types they see have resolved members. +func mayHaveLazyMembers(t *Type) bool { + return t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) == ObjectFlagsReference +} + +func (c *Checker) getReferenceMemberTypeArguments(t *Type, source *Type) (typeParameters []*Type, typeArguments []*Type) { + typeParameters = source.AsInterfaceType().allTypeParameters + typeArguments = c.getTypeArguments(t) + if len(typeArguments) == len(typeParameters)-1 { + typeArguments = core.Concatenate(typeArguments, []*Type{t}) + } + return typeParameters, typeArguments +} + +// getLazyMemberTable returns the lazy member table of t, preparing it first if +// needed, or nil if t's members aren't resolved lazily. A table returned while +// it is still being prepared isn't ready. +func (c *Checker) getLazyMemberTable(t *Type) *lazyMemberTable { + if !isUnresolvedInstantiatedReference(t) { + return nil + } + if lm := c.lazyMemberTables[t]; lm != nil { + return lm + } + typeParameters, typeArguments := c.getReferenceMemberTypeArguments(t, t.Target()) + if slices.Equal(typeParameters, typeArguments) { + return nil + } + // Resolving deferred type arguments may have led to t being resolved or + // prepared already. Like resolveObjectTypeMembers, which would then replace + // those members, prepare t (again) regardless. + lm := &lazyMemberTable{ + mapper: newTypeMapper(typeParameters, typeArguments), + typeArguments: typeArguments, + declared: map[string]*ast.Symbol{}, + found: map[string]*ast.Symbol{}, + } + c.lazyMemberTables[t] = lm + c.prepareLazyMembers(t, lm) + if lm.state != lazyMembersReady { + return nil + } + return lm +} + +// getReadyLazyMemberTable returns the lazy member table of t if it is ready. +func (c *Checker) getReadyLazyMemberTable(t *Type) *lazyMemberTable { + if !mayHaveLazyMembers(t) { + return nil + } + return c.getReadyLazyMemberTableWorker(t) +} + +func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { + if lm := c.getLazyMemberTable(t); lm != nil && lm.state == lazyMembersReady { + return lm + } + return nil +} + +// prepareLazyMembers mirrors resolveObjectTypeMembers, except that it only +// records which declared members would instantiate to themselves instead of +// instantiating them, and doesn't inherit members. +func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { + source := t.Target() + resolved := c.resolveDeclaredMembers(source) + for id, symbol := range resolved.declaredMembers { + if c.isNamedMember(symbol, id) && c.isSymbolUnaffectedByInstantiation(symbol, lm.mapper) { + lm.unaffected = append(lm.unaffected, id) + } + } + slices.Sort(lm.unaffected) + lm.callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, lm.mapper) + lm.constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, lm.mapper) + lm.indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, lm.mapper) + baseTypes := c.getBaseTypes(source) + lm.state = lazyMembersResolvingBases + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + // A lookup led to t being resolved in full; resolveObjectTypeMembers + // would now go on to replace those members with its own. + c.resolveLazyMembers(t, lm) + } + callSignatures, constructSignatures, indexInfos := lm.callSignatures, lm.constructSignatures, lm.indexInfos + thisArgument := core.LastOrNil(lm.typeArguments) + for _, baseType := range baseTypes { + instantiatedBaseType := baseType + if thisArgument != nil { + instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, lm.mapper), thisArgument, false /*needsApparentType*/) + } + lm.baseTypes = append(lm.baseTypes, instantiatedBaseType) + if lm.state == lazyMembersResolvingBases && !c.prepareBaseTypeMembers(instantiatedBaseType) { + // The base type's members are only partially resolved, and t must + // inherit them as they are now. + c.resolveLazyMembers(t, lm) + } + if lm.state == lazyMembersMaterialized { + lm.members = c.addInheritedMembers(lm.members, c.getPropertiesOfType(instantiatedBaseType)) + } + callSignatures, constructSignatures, indexInfos = c.appendInheritedSignaturesAndIndexInfos(callSignatures, constructSignatures, indexInfos, instantiatedBaseType) + lm.inheritedCount++ + } + lm.callSignatures, lm.constructSignatures, lm.indexInfos = callSignatures, constructSignatures, indexInfos + if lm.state == lazyMembersMaterialized { + t.objectFlags &^= ObjectFlagsUnresolvedMembers + c.setStructuredTypeMembers(t, lm.members, callSignatures, constructSignatures, indexInfos) + delete(c.lazyMemberTables, t) + return + } + lm.state = lazyMembersReady +} + +// prepareBaseTypeMembers resolves the members of an instantiated base type as +// inheriting from it in resolveObjectTypeMembers would. It returns false, and +// leaves that to the caller, when those members are only partially resolved. +func (c *Checker) prepareBaseTypeMembers(baseType *Type) bool { + if baseType == c.anyType { + return true + } + reduced := c.getReducedApparentType(baseType) + if reduced.flags&TypeFlagsObject != 0 { + if reduced.objectFlags&ObjectFlagsUnresolvedMembers != 0 { + return false + } + if lm := c.getLazyMemberTable(reduced); lm != nil { + return lm.state == lazyMembersReady + } + } + c.getPropertiesOfType(baseType) + c.getSignaturesOfType(baseType, SignatureKindCall) + c.getSignaturesOfType(baseType, SignatureKindConstruct) + c.getIndexInfosOfType(baseType) + return true +} + +// resolveLazyMembers resolves the members of t, whose lazy member table was +// prepared, as resolveObjectTypeMembers does. While the table's base types are +// still being prepared, it only exposes the members inherited so far, like +// resolveObjectTypeMembers would at that point, and prepareLazyMembers +// finishes the job. +func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { + source := t.Target() + resolved := c.resolveDeclaredMembers(source) + var members ast.SymbolTable + if len(resolved.declaredMembers) != 0 { + members = make(ast.SymbolTable, len(resolved.declaredMembers)) + for id, symbol := range resolved.declaredMembers { + if c.isNamedMember(symbol, id) { + members[id] = c.instantiateLazyDeclaredMember(lm, symbol, id) + } + } + } + if len(c.getBaseTypes(source)) != 0 { + c.setStructuredTypeMembers(t, members, lm.callSignatures, lm.constructSignatures, lm.indexInfos) + t.objectFlags |= ObjectFlagsUnresolvedMembers + for _, baseType := range lm.baseTypes[:lm.inheritedCount] { + members = c.addInheritedMembers(members, c.getPropertiesOfType(baseType)) + } + } + if lm.state != lazyMembersReady { + lm.state = lazyMembersMaterialized + lm.members = members + return + } + t.objectFlags &^= ObjectFlagsUnresolvedMembers + c.setStructuredTypeMembers(t, members, lm.callSignatures, lm.constructSignatures, lm.indexInfos) + delete(c.lazyMemberTables, t) +} + +// getLazyDeclaredMember instantiates the declared member symbol named name as +// resolveObjectTypeMembers would have when the table was prepared. +func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { + result := lm.declared[name] + if result == nil { + result = c.instantiateLazyDeclaredMember(lm, symbol, name) + lm.declared[name] = result + } + return result +} + +// instantiateLazyDeclaredMember is getLazyDeclaredMember without memoizing +// the result, for resolving the members in full, after which the table is +// discarded. +func (c *Checker) instantiateLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { + if result := lm.declared[name]; result != nil { + return result + } + if _, unaffected := slices.BinarySearch(lm.unaffected, name); unaffected { + return symbol + } + return c.newInstantiatedSymbol(symbol, lm.mapper) +} + +// getMemberOfStructuredType returns c.resolveStructuredTypeMembers(t).members[name] +// without resolving the members of t if they are resolved lazily. +func (c *Checker) getMemberOfStructuredType(t *Type, name string) *ast.Symbol { + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + return t.AsStructuredType().members[name] + } + return c.getMemberOfUnresolvedStructuredType(t, name) +} + +func (c *Checker) getMemberOfUnresolvedStructuredType(t *Type, name string) *ast.Symbol { + if symbol, ok := c.lookupMemberLazily(t, name); ok { + return symbol + } + return c.resolveStructuredTypeMembers(t).members[name] +} + +// lookupMemberLazily returns what resolveStructuredTypeMembers(t).members[name] +// would hold, without building t's member table. ok is false when the lazy +// path can't answer and the caller must resolve members normally. +func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, ok bool) { + if isReservedMemberName(name) { + return nil, false + } + lm := c.getLazyMemberTable(t) + if lm == nil || lm.state == lazyMembersResolvingDeclared { + return nil, false + } + if symbol, ok := lm.found[name]; ok { + return symbol, true + } + // Mirrors resolveObjectTypeMembers: declared named members first, then + // inherited properties, where a base may only fill a missing or non-value + // entry (addInheritedMembers). + resolved := c.resolveDeclaredMembers(t.Target()) + var result *ast.Symbol + if decl := resolved.declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { + result = c.getLazyDeclaredMember(lm, decl, name) + } + baseTypes := lm.baseTypes[:lm.inheritedCount] + if lm.state != lazyMembersReady && len(resolved.declaredMembers) == 0 { + // While base types are being resolved, resolveObjectTypeMembers only + // exposes inherited members through the table of declared members. + baseTypes = nil + } + for _, baseType := range baseTypes { + if result != nil && result.Flags&ast.SymbolFlagsValue != 0 { + break + } + if prop := c.getNamedPropertyOfType(baseType, name); prop != nil && isInheritableProperty(prop) { + result = prop + } + } + if lm.state == lazyMembersReady { + lm.found[name] = result + } + return result, true +} + +// getNamedPropertyOfType finds name among getPropertiesOfType(t). +func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { + reduced := c.getReducedApparentType(t) + if reduced.flags&TypeFlagsObject != 0 { + if symbol := c.getMemberOfStructuredType(reduced, name); symbol != nil && c.isNamedMember(symbol, name) { + return symbol + } + return nil + } + for _, prop := range c.getPropertiesOfType(t) { + if prop.Name == name { + return prop + } + } + return nil +} + +// everyPropertyOfStructuredType is core.Every(c.resolveStructuredTypeMembers(t).properties, f), +// without resolving the members of t if they are resolved lazily. f may see a +// declared member instead of its instantiation, which has the same flags. +func (c *Checker) everyPropertyOfStructuredType(t *Type, f func(prop *ast.Symbol) bool) bool { + if lm := c.getReadyLazyMemberTable(t); lm != nil { + var seen collections.Set[string] + return c.everyLazyProperty(t, lm, &seen, f) + } + return core.Every(c.resolveStructuredTypeMembers(t).properties, f) +} + +func (c *Checker) hasPropertiesOfStructuredType(t *Type) bool { + return !c.everyPropertyOfStructuredType(t, func(*ast.Symbol) bool { return false }) +} + +// everyLazyProperty reports whether f holds for every property of t. As in +// addInheritedMembers, a property hides inherited properties of the same +// name; seen tracks those names. +func (c *Checker) everyLazyProperty(t *Type, lm *lazyMemberTable, seen *collections.Set[string], f func(prop *ast.Symbol) bool) bool { + for id, symbol := range c.resolveDeclaredMembers(t.Target()).declaredMembers { + if c.isNamedMember(symbol, id) && seen.AddIfAbsent(id) && !f(symbol) { + return false + } + } + for _, baseType := range lm.baseTypes { + reduced := c.getReducedApparentType(baseType) + if baseTable := c.getReadyLazyMemberTable(reduced); baseTable != nil { + if !c.everyLazyProperty(reduced, baseTable, seen, f) { + return false + } + continue + } + for _, prop := range c.getPropertiesOfType(baseType) { + if isInheritableProperty(prop) && seen.AddIfAbsent(prop.Name) && !f(prop) { + return false + } + } + } + return true +} + +func isInheritableProperty(prop *ast.Symbol) bool { + return !isStaticPrivateIdentifierProperty(prop) +} + func (c *Checker) getBaseTypes(t *Type) []*Type { if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 { return nil @@ -19704,22 +20077,14 @@ func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature { func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature { if t.flags&TypeFlagsObject != 0 { - if shape := c.getLazyShape(t); shape != nil { - if !allowMembers && (shape.hasProperties || len(c.getIndexInfosOfStructuredType(t)) != 0) { - return nil + if allowMembers || !c.hasPropertiesOfStructuredType(t) && len(c.getIndexInfosOfStructuredType(t)) == 0 { + callSignatures := c.getSignaturesOfStructuredType(t, SignatureKindCall) + constructSignatures := c.getSignaturesOfStructuredType(t, SignatureKindConstruct) + if kind == SignatureKindCall && len(callSignatures) == 1 && len(constructSignatures) == 0 { + return callSignatures[0] } - calls, constructs := shape.callSignatureCount, shape.constructSignatureCount - if !(kind == SignatureKindCall && calls == 1 && constructs == 0 || kind == SignatureKindConstruct && constructs == 1 && calls == 0) { - return nil - } - } - resolved := c.resolveStructuredTypeMembers(t) - if allowMembers || len(resolved.properties) == 0 && len(resolved.indexInfos) == 0 { - if kind == SignatureKindCall && len(resolved.CallSignatures()) == 1 && len(resolved.ConstructSignatures()) == 0 { - return resolved.CallSignatures()[0] - } - if kind == SignatureKindConstruct && len(resolved.ConstructSignatures()) == 1 && len(resolved.CallSignatures()) == 0 { - return resolved.ConstructSignatures()[0] + if kind == SignatureKindConstruct && len(constructSignatures) == 1 && len(callSignatures) == 0 { + return constructSignatures[0] } } } @@ -21775,14 +22140,6 @@ func (c *Checker) includeMixinType(t *Type, types []*Type, mixinFlags []bool, in */ func (c *Checker) getPropertyOfObjectType(t *Type, name string) *ast.Symbol { if t.flags&TypeFlagsObject != 0 { - if mayHaveLazyMembers(t) { - if symbol, ok := c.lookupMemberLazily(t, name); ok { - if symbol != nil && c.symbolIsValue(symbol) { - return symbol - } - return nil - } - } resolved := c.resolveStructuredTypeMembers(t) symbol := resolved.members[name] if symbol != nil && c.symbolIsValue(symbol) { @@ -26932,9 +27289,6 @@ func (c *Checker) isEmptyResolvedType(t *StructuredType) bool { func (c *Checker) isEmptyObjectType(t *Type) bool { switch { case t.flags&TypeFlagsObject != 0: - if shape := c.getLazyShape(t); shape != nil { - return !shape.hasProperties && shape.callSignatureCount+shape.constructSignatureCount == 0 && len(c.getIndexInfosOfStructuredType(t)) == 0 - } return !c.isGenericMappedType(t) && c.isEmptyResolvedType(c.resolveStructuredTypeMembers(t)) case t.flags&TypeFlagsNonPrimitive != 0: return true @@ -27813,10 +28167,7 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node } func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { - if shape := c.getLazyShape(t); shape != nil && (shape.hasProperties || len(c.getIndexInfosOfStructuredType(t)) == 0) { - return false - } - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.getPropertiesOfType(t)) == 0 && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || + return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && !c.hasPropertiesOfStructuredType(t) && len(c.getIndexInfosOfType(t)) == 1 && c.getIndexInfoOfType(t, c.stringType) != nil || t.flags&TypeFlagsUnionOrIntersection != 0 && core.Every(t.Types(), c.isStringIndexSignatureOnlyType) } @@ -31596,16 +31947,8 @@ func (c *Checker) isFunctionObjectType(t *Type) bool { } // We do a quick check for a "bind" property before performing the more expensive subtype // check. This gives us a quicker out in the common case where an object type is not a function. - if shape := c.getLazyShape(t); shape != nil { - if shape.callSignatureCount+shape.constructSignatureCount != 0 { - return true - } - if bind, ok := c.lookupMemberLazily(t, "bind"); ok && bind == nil { - return false - } - } - resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.signatures) != 0 || resolved.members["bind"] != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) + return len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) != 0 || len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) != 0 || + c.getMemberOfStructuredType(t, "bind") != nil && c.isTypeSubtypeOf(t, c.globalFunctionType) } func (c *Checker) getTypeWithFacts(t *Type, include TypeFacts) *Type { diff --git a/tsc/internal/checker/lazymembers.go b/tsc/internal/checker/lazymembers.go deleted file mode 100644 index 6ebc7b446766c..0000000000000 --- a/tsc/internal/checker/lazymembers.go +++ /dev/null @@ -1,498 +0,0 @@ -package checker - -import ( - "slices" - - "github.com/microsoft/TypeScript/tsc/internal/ast" - "github.com/microsoft/TypeScript/tsc/internal/collections" - "github.com/microsoft/TypeScript/tsc/internal/core" -) - -// Resolving the members of an instantiated class or interface reference -// instantiates every declared member and merges every inherited one, even when -// the caller only wants one property (`expect(x).toBe`, `schema.optional`, -// `arr.map`). In large programs most of those symbols are never used. -// -// Instead, the first lookup on such a reference prepares a lazy member table: -// it does everything resolveObjectTypeMembers would do except create the -// member symbols, in the same order, so everything that gets resolved along -// the way (signatures, index infos, base types and their members) is resolved -// exactly as before. Lookups then instantiate only the requested member, the -// same way resolveObjectTypeMembers would have at the time the table was -// prepared, and if the members are resolved in full later, the symbols -// already handed out are reused. Should preparing the table lead back to the -// reference, the reference exposes the same partial members that -// resolveObjectTypeMembers would, and is from then on resolved in full. - -type lazyMembersState int - -const ( - lazyMembersResolvingDeclared lazyMembersState = iota // instantiating declared signatures and index infos, resolving base types - lazyMembersResolvingBases // instantiating base types and resolving their members - lazyMembersReady - lazyMembersMaterialized // resolving the members in full, as resolveObjectTypeMembers does -) - -type lazyMemberTable struct { - state lazyMembersState - mapper *TypeMapper - typeArguments []*Type - unaffected []string // sorted names of declared members that instantiate to themselves - callSignatures []*Signature - constructSignatures []*Signature - indexInfos []*IndexInfo - baseTypes []*Type // instantiated base types - inheritedCount int // number of base types whose members have been inherited - declared map[string]*ast.Symbol - found map[string]*ast.Symbol // memoized lookups; nil means no such member - resolving *resolvingMembers - shape *lazyShape - allIndexInfos []*IndexInfo // declared and inherited index infos, once computed - allIndexInfosDone bool -} - -// lazyShape describes the members of a reference whose lazy member table is -// ready, as far as that is known without resolving them. -type lazyShape struct { - callSignatureCount int - constructSignatureCount int - hasProperties bool - optionalityDone bool - onlyOptionalProperties bool -} - -// resolvingMembers holds the members of a lazily prepared type that is being -// resolved in full. -type resolvingMembers struct { - members ast.SymbolTable - callSignatures []*Signature - constructSignatures []*Signature - indexInfos []*IndexInfo -} - -// isUnresolvedInstantiatedReference reports whether t is a reference to a -// generic class or interface (other than the declaration itself) whose members -// haven't been resolved. -func isUnresolvedInstantiatedReference(t *Type) bool { - if t.flags&TypeFlagsObject == 0 || !mayHaveLazyMembers(t) { - return false - } - source := t.Target() - if source == nil || source == t || source.objectFlags&ObjectFlagsClassOrInterface == 0 || source.objectFlags&ObjectFlagsTuple != 0 { - return false - } - return t.symbol == nil || t.symbol.Flags&ast.SymbolFlagsValueModule == 0 -} - -// mayHaveLazyMembers is a quick check for isUnresolvedInstantiatedReference -// that callers on hot paths make first, as most types they see have resolved -// members. -func mayHaveLazyMembers(t *Type) bool { - return t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) == ObjectFlagsReference -} - -func (c *Checker) getReferenceMemberTypeArguments(t *Type, source *Type) (typeParameters []*Type, typeArguments []*Type) { - typeParameters = source.AsInterfaceType().allTypeParameters - typeArguments = c.getTypeArguments(t) - if len(typeArguments) == len(typeParameters)-1 { - typeArguments = core.Concatenate(typeArguments, []*Type{t}) - } - return typeParameters, typeArguments -} - -// getLazyMemberTable returns the lazy member table of t, preparing it first if -// needed, or nil if t's members aren't resolved lazily. A table returned while -// it is still being prepared isn't ready. -func (c *Checker) getLazyMemberTable(t *Type) *lazyMemberTable { - if !isUnresolvedInstantiatedReference(t) { - return nil - } - if lm := c.lazyMemberTables[t]; lm != nil { - return lm - } - typeParameters, typeArguments := c.getReferenceMemberTypeArguments(t, t.Target()) - if slices.Equal(typeParameters, typeArguments) { - return nil - } - // Resolving deferred type arguments may have led to t being resolved or - // prepared already. Like resolveObjectTypeMembers, which would then replace - // those members, prepare t (again) regardless. - lm := &lazyMemberTable{ - mapper: newTypeMapper(typeParameters, typeArguments), - typeArguments: typeArguments, - declared: map[string]*ast.Symbol{}, - found: map[string]*ast.Symbol{}, - } - c.lazyMemberTables[t] = lm - c.prepareLazyMembers(t, lm) - if lm.state != lazyMembersReady { - return nil - } - return lm -} - -// prepareLazyMembers mirrors resolveObjectTypeMembers, except that it only -// records which declared members would instantiate to themselves instead of -// instantiating them. -func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { - source := t.Target() - resolved := c.resolveDeclaredMembers(source) - for id, symbol := range resolved.declaredMembers { - if c.isNamedMember(symbol, id) && c.isSymbolUnaffectedByInstantiation(symbol, lm.mapper) { - lm.unaffected = append(lm.unaffected, id) - } - } - slices.Sort(lm.unaffected) - lm.callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, lm.mapper) - lm.constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, lm.mapper) - lm.indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, lm.mapper) - baseTypes := c.getBaseTypes(source) - lm.state = lazyMembersResolvingBases - if t.objectFlags&ObjectFlagsMembersResolved != 0 { - // A lookup led to t being resolved in full; resolveObjectTypeMembers - // would now go on to replace those members with its own. - c.resolveLazyMembers(t, lm) - } - thisArgument := core.LastOrNil(lm.typeArguments) - for _, baseType := range baseTypes { - instantiatedBaseType := baseType - if thisArgument != nil { - instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, lm.mapper), thisArgument, false /*needsApparentType*/) - } - lm.baseTypes = append(lm.baseTypes, instantiatedBaseType) - if lm.state == lazyMembersResolvingBases && !c.prepareBaseTypeMembers(instantiatedBaseType) { - // The base type's members are only partially resolved, and t must - // inherit them as they are now. - c.resolveLazyMembers(t, lm) - } - if lm.state == lazyMembersMaterialized { - c.inheritBaseTypeMembers(lm.resolving, instantiatedBaseType) - } - lm.inheritedCount++ - } - if lm.state == lazyMembersMaterialized { - t.objectFlags &^= ObjectFlagsUnresolvedMembers - c.setStructuredTypeMembers(t, lm.resolving.members, lm.resolving.callSignatures, lm.resolving.constructSignatures, lm.resolving.indexInfos) - delete(c.lazyMemberTables, t) - return - } - lm.state = lazyMembersReady -} - -// prepareBaseTypeMembers resolves the members of an instantiated base type as -// inheriting from it in resolveObjectTypeMembers would. It returns false, and -// leaves that to the caller, when those members are only partially resolved. -func (c *Checker) prepareBaseTypeMembers(baseType *Type) bool { - if baseType == c.anyType { - return true - } - reduced := c.getReducedApparentType(baseType) - if reduced.flags&TypeFlagsObject != 0 { - if reduced.objectFlags&ObjectFlagsUnresolvedMembers != 0 { - return false - } - if lm := c.getLazyMemberTable(reduced); lm != nil { - return lm.state == lazyMembersReady - } - } - c.getPropertiesOfType(baseType) - c.getSignaturesOfType(baseType, SignatureKindCall) - c.getSignaturesOfType(baseType, SignatureKindConstruct) - c.getIndexInfosOfType(baseType) - return true -} - -// resolveLazyMembers resolves the members of t, whose lazy member table is -// ready, as resolveObjectTypeMembers does. While the table's base types are -// still being prepared, it only exposes the members inherited so far, like -// resolveObjectTypeMembers would at that point, and prepareLazyMembers -// finishes the job. -func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { - source := t.Target() - resolved := c.resolveDeclaredMembers(source) - var members ast.SymbolTable - if len(resolved.declaredMembers) != 0 { - members = make(ast.SymbolTable, len(resolved.declaredMembers)) - for id, symbol := range resolved.declaredMembers { - if c.isNamedMember(symbol, id) { - members[id] = c.instantiateLazyDeclaredMember(lm, symbol, id) - } - } - } - r := &resolvingMembers{ - members: members, - callSignatures: lm.callSignatures, - constructSignatures: lm.constructSignatures, - indexInfos: lm.indexInfos, - } - if len(c.getBaseTypes(source)) != 0 { - c.setStructuredTypeMembers(t, r.members, r.callSignatures, r.constructSignatures, r.indexInfos) - t.objectFlags |= ObjectFlagsUnresolvedMembers - for _, baseType := range lm.baseTypes[:lm.inheritedCount] { - c.inheritBaseTypeMembers(r, baseType) - } - } - if lm.state != lazyMembersReady { - lm.state = lazyMembersMaterialized - lm.resolving = r - return - } - t.objectFlags &^= ObjectFlagsUnresolvedMembers - c.setStructuredTypeMembers(t, r.members, r.callSignatures, r.constructSignatures, r.indexInfos) - delete(c.lazyMemberTables, t) -} - -// inheritBaseTypeMembers is the body of the base type loop in -// resolveObjectTypeMembers. -func (c *Checker) inheritBaseTypeMembers(r *resolvingMembers, baseType *Type) { - r.members = c.addInheritedMembers(r.members, c.getPropertiesOfType(baseType)) - r.callSignatures = core.Concatenate(r.callSignatures, c.getSignaturesOfType(baseType, SignatureKindCall)) - r.constructSignatures = core.Concatenate(r.constructSignatures, c.getSignaturesOfType(baseType, SignatureKindConstruct)) - var inheritedIndexInfos []*IndexInfo - if baseType != c.anyType { - inheritedIndexInfos = c.getIndexInfosOfType(baseType) - } else { - inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} - } - r.indexInfos = core.Concatenate(r.indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { - return findIndexInfo(r.indexInfos, info.keyType) == nil - })) -} - -// getLazyDeclaredMember instantiates the declared member symbol named name as -// resolveObjectTypeMembers would have when the table was prepared. -func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { - result := lm.declared[name] - if result == nil { - result = c.instantiateLazyDeclaredMember(lm, symbol, name) - lm.declared[name] = result - } - return result -} - -// instantiateLazyDeclaredMember is getLazyDeclaredMember without memoizing -// the result, for resolving the members in full, after which the table is -// discarded. -func (c *Checker) instantiateLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { - if result := lm.declared[name]; result != nil { - return result - } - if _, unaffected := slices.BinarySearch(lm.unaffected, name); unaffected { - return symbol - } - return c.newInstantiatedSymbol(symbol, lm.mapper) -} - -// lookupMemberLazily returns what resolveStructuredTypeMembers(t).members[name] -// would hold, without building t's member table. ok is false when the lazy -// path can't answer and the caller must resolve members normally. -func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, ok bool) { - if isReservedMemberName(name) { - return nil, false - } - lm := c.getLazyMemberTable(t) - if lm == nil || lm.state == lazyMembersResolvingDeclared { - return nil, false - } - if symbol, ok := lm.found[name]; ok { - return symbol, true - } - // Mirrors resolveObjectTypeMembers: declared named members first, then - // inherited properties, where a base may only fill a missing or non-value - // entry (addInheritedMembers). - resolved := c.resolveDeclaredMembers(t.Target()) - var result *ast.Symbol - if decl := resolved.declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { - result = c.getLazyDeclaredMember(lm, decl, name) - } - baseTypes := lm.baseTypes[:lm.inheritedCount] - if lm.state != lazyMembersReady && len(resolved.declaredMembers) == 0 { - // While base types are being resolved, resolveObjectTypeMembers only - // exposes inherited members through the table of declared members. - baseTypes = nil - } - for _, baseType := range baseTypes { - if result != nil && result.Flags&ast.SymbolFlagsValue != 0 { - break - } - if prop := c.getNamedPropertyOfType(baseType, name); prop != nil && !isStaticPrivateIdentifierProperty(prop) { - result = prop - } - } - if lm.state == lazyMembersReady { - lm.found[name] = result - } - return result, true -} - -// getPropertyOfObjectTypeLazily returns what getPropertyOfTypeEx returns for -// the object type t without resolving its members. ok is false when the lazy -// path can't answer. -func (c *Checker) getPropertyOfObjectTypeLazily(t *Type, name string, skipObjectFunctionPropertyAugment bool, includeTypeOnlyMembers bool) (symbol *ast.Symbol, ok bool) { - symbol, ok = c.lookupMemberLazily(t, name) - if !ok { - return nil, false - } - if symbol != nil && c.symbolIsValueEx(symbol, includeTypeOnlyMembers) { - return symbol, true - } - if skipObjectFunctionPropertyAugment { - return nil, true - } - if symbol != nil { - return nil, false - } - shape := c.getLazyShape(t) - if shape == nil { - return nil, false - } - var functionType *Type - switch { - case shape.callSignatureCount != 0: - functionType = c.globalCallableFunctionType - case shape.constructSignatureCount != 0: - functionType = c.globalNewableFunctionType - } - if functionType != nil { - if symbol := c.getPropertyOfObjectType(functionType, name); symbol != nil { - return symbol, true - } - } - return c.getPropertyOfObjectType(c.globalObjectType, name), true -} - -// getIndexInfosLazily returns resolveStructuredTypeMembers(t).indexInfos -// without resolving t's members (e.g. for `arr[i]` on an Array). -func (c *Checker) getIndexInfosLazily(t *Type) ([]*IndexInfo, bool) { - lm := c.getLazyMemberTable(t) - if lm == nil || lm.state != lazyMembersReady { - return nil, false - } - if !lm.allIndexInfosDone { - // Mirrors resolveObjectTypeMembers. - indexInfos := lm.indexInfos - for _, baseType := range lm.baseTypes { - var inheritedIndexInfos []*IndexInfo - if baseType != c.anyType { - inheritedIndexInfos = c.getIndexInfosOfType(baseType) - } else { - inheritedIndexInfos = []*IndexInfo{c.anyBaseTypeIndexInfo} - } - indexInfos = core.Concatenate(indexInfos, core.Filter(inheritedIndexInfos, func(info *IndexInfo) bool { - return findIndexInfo(indexInfos, info.keyType) == nil - })) - } - lm.allIndexInfos = indexInfos - lm.allIndexInfosDone = true - } - return lm.allIndexInfos, true -} - -// getNamedPropertyOfType finds name among getPropertiesOfType(t). -func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { - reduced := c.getReducedApparentType(t) - if reduced.flags&TypeFlagsObject != 0 { - if symbol, ok := c.lookupMemberLazily(reduced, name); ok { - return symbol - } - if symbol := c.resolveStructuredTypeMembers(reduced).members[name]; symbol != nil && c.isNamedMember(symbol, name) { - return symbol - } - return nil - } - for _, prop := range c.getPropertiesOfType(t) { - if prop.Name == name { - return prop - } - } - return nil -} - -// getLazyShape returns the shape of t when t has a lazy member table that is -// ready. It is computed from the declared members and signatures and those -// of the prepared base types, which resolveObjectTypeMembers concatenates -// (signatures) or merges (properties). -func (c *Checker) getLazyShape(t *Type) *lazyShape { - if !mayHaveLazyMembers(t) { - return nil - } - return c.getLazyShapeWorker(t) -} - -func (c *Checker) getLazyShapeWorker(t *Type) *lazyShape { - lm := c.getLazyMemberTable(t) - if lm == nil || lm.state != lazyMembersReady { - return nil - } - if lm.shape == nil { - shape := &lazyShape{ - callSignatureCount: len(lm.callSignatures), - constructSignatureCount: len(lm.constructSignatures), - } - for id, symbol := range c.resolveDeclaredMembers(t.Target()).declaredMembers { - if c.isNamedMember(symbol, id) { - shape.hasProperties = true - break - } - } - for _, baseType := range lm.baseTypes { - if baseShape := c.getLazyShape(c.getReducedApparentType(baseType)); baseShape != nil { - shape.callSignatureCount += baseShape.callSignatureCount - shape.constructSignatureCount += baseShape.constructSignatureCount - shape.hasProperties = shape.hasProperties || baseShape.hasProperties - } else { - shape.callSignatureCount += len(c.getSignaturesOfType(baseType, SignatureKindCall)) - shape.constructSignatureCount += len(c.getSignaturesOfType(baseType, SignatureKindConstruct)) - shape.hasProperties = shape.hasProperties || core.Some(c.getPropertiesOfType(baseType), isInheritableProperty) - } - } - lm.shape = shape - } - return lm.shape -} - -func isInheritableProperty(prop *ast.Symbol) bool { - return !isStaticPrivateIdentifierProperty(prop) -} - -// hasOnlyOptionalLazyProperties reports whether every property of t, which -// has a lazy member table that is ready, is optional. -func (c *Checker) hasOnlyOptionalLazyProperties(t *Type) bool { - shape := c.getLazyShape(t) - if !shape.optionalityDone { - var seen collections.Set[string] - shape.onlyOptionalProperties = c.everyLazyProperty(t, &seen, func(prop *ast.Symbol) bool { - return prop.Flags&ast.SymbolFlagsOptional != 0 - }) - shape.optionalityDone = true - } - return shape.onlyOptionalProperties -} - -// everyLazyProperty reports whether f holds for every property of t, which has -// a lazy member table that is ready. As in addInheritedMembers, a property -// hides inherited properties of the same name; seen tracks those names. A -// declared member is passed as declared, which has the same flags as its -// instantiation. -func (c *Checker) everyLazyProperty(t *Type, seen *collections.Set[string], f func(prop *ast.Symbol) bool) bool { - for id, symbol := range c.resolveDeclaredMembers(t.Target()).declaredMembers { - if c.isNamedMember(symbol, id) && seen.AddIfAbsent(id) && !f(symbol) { - return false - } - } - for _, baseType := range c.lazyMemberTables[t].baseTypes { - reduced := c.getReducedApparentType(baseType) - if c.getLazyShape(reduced) != nil { - if !c.everyLazyProperty(reduced, seen, f) { - return false - } - continue - } - for _, prop := range c.getPropertiesOfType(baseType) { - if isInheritableProperty(prop) && seen.AddIfAbsent(prop.Name) && !f(prop) { - return false - } - } - } - return true -} diff --git a/tsc/internal/checker/relater.go b/tsc/internal/checker/relater.go index 194cf2e00a820..c959dd60b249b 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -678,11 +678,8 @@ func (c *Checker) elaborateArrowFunction(node *ast.Node, source *Type, target *T // and no required properties, call/construct signatures or index signatures func (c *Checker) isWeakType(t *Type) bool { if t.flags&TypeFlagsObject != 0 { - if shape := c.getLazyShape(t); shape != nil { - return shape.callSignatureCount+shape.constructSignatureCount == 0 && len(c.getIndexInfosOfStructuredType(t)) == 0 && shape.hasProperties && c.hasOnlyOptionalLazyProperties(t) - } - resolved := c.resolveStructuredTypeMembers(t) - return len(resolved.signatures) == 0 && len(resolved.indexInfos) == 0 && len(resolved.properties) > 0 && core.Every(resolved.properties, func(p *ast.Symbol) bool { + return len(c.getSignaturesOfStructuredType(t, SignatureKindCall)) == 0 && len(c.getSignaturesOfStructuredType(t, SignatureKindConstruct)) == 0 && + len(c.getIndexInfosOfStructuredType(t)) == 0 && c.hasPropertiesOfStructuredType(t) && c.everyPropertyOfStructuredType(t, func(p *ast.Symbol) bool { return p.Flags&ast.SymbolFlagsOptional != 0 }) } From 39ce30c883e26a35e74847e76a6299338407c72a Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Tue, 29 Sep 2026 01:17:43 -0400 Subject: [PATCH 6/9] Drop the partial member handling now that #64372 is in resolveObjectTypeMembers no longer exposes a type's declared members while its base types resolve, so the lazy table doesn't need to either. A table is now either being prepared, during which the type resolves its members as usual, or ready; the materialized state, the count of inherited base types and the partial lookup paths go away. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 219 +++++++++----------------------- 1 file changed, 63 insertions(+), 156 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index f43134b7bf19f..3829bf75ff247 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19443,7 +19443,7 @@ func (c *Checker) resolveClassOrInterfaceMembers(t *Type) { } func (c *Checker) resolveTypeReferenceMembers(t *Type) { - if lm := c.lazyMemberTables[t]; lm != nil && lm.state != lazyMembersResolvingDeclared { + if lm := c.lazyMemberTables[t]; lm != nil && lm.ready { c.resolveLazyMembers(t, lm) return } @@ -19530,35 +19530,20 @@ func (c *Checker) appendInheritedSignaturesAndIndexInfos(callSignatures []*Signa // getIndexInfosOfStructuredType answer from the table. Member lookups // instantiate only the requested member, the way resolveObjectTypeMembers // would have when the table was prepared, and resolving the members in full -// later reuses the symbols already handed out. -// -// While resolveObjectTypeMembers resolves base types, the type exposes its -// declared members (see ObjectFlagsUnresolvedMembers). If preparing the table -// leads back to the type, the type is resolved in full at that point so it -// exposes the same members. - -type lazyMembersState int - -const ( - lazyMembersResolvingDeclared lazyMembersState = iota // instantiating declared signatures and index infos, resolving base types - lazyMembersResolvingBases // instantiating base types and resolving their members - lazyMembersReady - lazyMembersMaterialized // resolved in full while base types were being resolved -) +// later reuses the symbols already handed out. While a table is being +// prepared, the type resolves its members as usual. type lazyMemberTable struct { - state lazyMembersState + ready bool mapper *TypeMapper typeArguments []*Type unaffected []string // sorted names of declared members that instantiate to themselves - callSignatures []*Signature // declared, and once the table is ready, inherited + callSignatures []*Signature // declared and inherited constructSignatures []*Signature indexInfos []*IndexInfo baseTypes []*Type // instantiated base types - inheritedCount int // number of base types inherited from so far declared map[string]*ast.Symbol found map[string]*ast.Symbol // memoized lookups; nil means no such member - members ast.SymbolTable // once materialized } // isUnresolvedInstantiatedReference reports whether t is a reference to a @@ -19590,38 +19575,9 @@ func (c *Checker) getReferenceMemberTypeArguments(t *Type, source *Type) (typePa return typeParameters, typeArguments } -// getLazyMemberTable returns the lazy member table of t, preparing it first if -// needed, or nil if t's members aren't resolved lazily. A table returned while -// it is still being prepared isn't ready. -func (c *Checker) getLazyMemberTable(t *Type) *lazyMemberTable { - if !isUnresolvedInstantiatedReference(t) { - return nil - } - if lm := c.lazyMemberTables[t]; lm != nil { - return lm - } - typeParameters, typeArguments := c.getReferenceMemberTypeArguments(t, t.Target()) - if slices.Equal(typeParameters, typeArguments) { - return nil - } - // Resolving deferred type arguments may have led to t being resolved or - // prepared already. Like resolveObjectTypeMembers, which would then replace - // those members, prepare t (again) regardless. - lm := &lazyMemberTable{ - mapper: newTypeMapper(typeParameters, typeArguments), - typeArguments: typeArguments, - declared: map[string]*ast.Symbol{}, - found: map[string]*ast.Symbol{}, - } - c.lazyMemberTables[t] = lm - c.prepareLazyMembers(t, lm) - if lm.state != lazyMembersReady { - return nil - } - return lm -} - -// getReadyLazyMemberTable returns the lazy member table of t if it is ready. +// getReadyLazyMemberTable returns the lazy member table of t, preparing it +// first if needed, or nil if t's members aren't resolved lazily or the table +// is still being prepared. func (c *Checker) getReadyLazyMemberTable(t *Type) *lazyMemberTable { if !mayHaveLazyMembers(t) { return nil @@ -19630,15 +19586,34 @@ func (c *Checker) getReadyLazyMemberTable(t *Type) *lazyMemberTable { } func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { - if lm := c.getLazyMemberTable(t); lm != nil && lm.state == lazyMembersReady { - return lm + if !isUnresolvedInstantiatedReference(t) { + return nil } - return nil + lm := c.lazyMemberTables[t] + if lm == nil { + typeParameters, typeArguments := c.getReferenceMemberTypeArguments(t, t.Target()) + if slices.Equal(typeParameters, typeArguments) { + return nil + } + lm = &lazyMemberTable{ + mapper: newTypeMapper(typeParameters, typeArguments), + typeArguments: typeArguments, + declared: map[string]*ast.Symbol{}, + found: map[string]*ast.Symbol{}, + } + c.lazyMemberTables[t] = lm + c.prepareLazyMembers(t, lm) + } + if !lm.ready || t.objectFlags&ObjectFlagsMembersResolved != 0 { + return nil + } + return lm } -// prepareLazyMembers mirrors resolveObjectTypeMembers, except that it only -// records which declared members would instantiate to themselves instead of -// instantiating them, and doesn't inherit members. +// prepareLazyMembers mirrors resolveObjectTypeMembers, except that it records +// which declared members would instantiate to themselves instead of +// instantiating them, and doesn't inherit members from base types with lazy +// member tables. func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { source := t.Target() resolved := c.resolveDeclaredMembers(source) @@ -19648,76 +19623,34 @@ func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { } } slices.Sort(lm.unaffected) - lm.callSignatures = c.instantiateSignatures(resolved.declaredCallSignatures, lm.mapper) - lm.constructSignatures = c.instantiateSignatures(resolved.declaredConstructSignatures, lm.mapper) - lm.indexInfos = c.instantiateIndexInfos(resolved.declaredIndexInfos, lm.mapper) - baseTypes := c.getBaseTypes(source) - lm.state = lazyMembersResolvingBases - if t.objectFlags&ObjectFlagsMembersResolved != 0 { - // A lookup led to t being resolved in full; resolveObjectTypeMembers - // would now go on to replace those members with its own. - c.resolveLazyMembers(t, lm) - } - callSignatures, constructSignatures, indexInfos := lm.callSignatures, lm.constructSignatures, lm.indexInfos + callSignatures := c.instantiateSignatures(resolved.declaredCallSignatures, lm.mapper) + constructSignatures := c.instantiateSignatures(resolved.declaredConstructSignatures, lm.mapper) + indexInfos := c.instantiateIndexInfos(resolved.declaredIndexInfos, lm.mapper) thisArgument := core.LastOrNil(lm.typeArguments) - for _, baseType := range baseTypes { + for _, baseType := range c.getBaseTypes(source) { instantiatedBaseType := baseType if thisArgument != nil { instantiatedBaseType = c.getTypeWithThisArgument(c.instantiateType(baseType, lm.mapper), thisArgument, false /*needsApparentType*/) } lm.baseTypes = append(lm.baseTypes, instantiatedBaseType) - if lm.state == lazyMembersResolvingBases && !c.prepareBaseTypeMembers(instantiatedBaseType) { - // The base type's members are only partially resolved, and t must - // inherit them as they are now. - c.resolveLazyMembers(t, lm) - } - if lm.state == lazyMembersMaterialized { - lm.members = c.addInheritedMembers(lm.members, c.getPropertiesOfType(instantiatedBaseType)) + if c.getReadyLazyMemberTable(c.getReducedApparentType(instantiatedBaseType)) == nil { + c.getPropertiesOfType(instantiatedBaseType) } callSignatures, constructSignatures, indexInfos = c.appendInheritedSignaturesAndIndexInfos(callSignatures, constructSignatures, indexInfos, instantiatedBaseType) - lm.inheritedCount++ } lm.callSignatures, lm.constructSignatures, lm.indexInfos = callSignatures, constructSignatures, indexInfos - if lm.state == lazyMembersMaterialized { - t.objectFlags &^= ObjectFlagsUnresolvedMembers - c.setStructuredTypeMembers(t, lm.members, callSignatures, constructSignatures, indexInfos) - delete(c.lazyMemberTables, t) - return - } - lm.state = lazyMembersReady -} - -// prepareBaseTypeMembers resolves the members of an instantiated base type as -// inheriting from it in resolveObjectTypeMembers would. It returns false, and -// leaves that to the caller, when those members are only partially resolved. -func (c *Checker) prepareBaseTypeMembers(baseType *Type) bool { - if baseType == c.anyType { - return true - } - reduced := c.getReducedApparentType(baseType) - if reduced.flags&TypeFlagsObject != 0 { - if reduced.objectFlags&ObjectFlagsUnresolvedMembers != 0 { - return false - } - if lm := c.getLazyMemberTable(reduced); lm != nil { - return lm.state == lazyMembersReady - } + lm.ready = true + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + // Preparing the table led to t being resolved in full, and + // resolveObjectTypeMembers would now replace those members. + c.resolveLazyMembers(t, lm) } - c.getPropertiesOfType(baseType) - c.getSignaturesOfType(baseType, SignatureKindCall) - c.getSignaturesOfType(baseType, SignatureKindConstruct) - c.getIndexInfosOfType(baseType) - return true } -// resolveLazyMembers resolves the members of t, whose lazy member table was -// prepared, as resolveObjectTypeMembers does. While the table's base types are -// still being prepared, it only exposes the members inherited so far, like -// resolveObjectTypeMembers would at that point, and prepareLazyMembers -// finishes the job. +// resolveLazyMembers resolves the members of t from its lazy member table, as +// resolveObjectTypeMembers does. func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { - source := t.Target() - resolved := c.resolveDeclaredMembers(source) + resolved := c.resolveDeclaredMembers(t.Target()) var members ast.SymbolTable if len(resolved.declaredMembers) != 0 { members = make(ast.SymbolTable, len(resolved.declaredMembers)) @@ -19727,19 +19660,9 @@ func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { } } } - if len(c.getBaseTypes(source)) != 0 { - c.setStructuredTypeMembers(t, members, lm.callSignatures, lm.constructSignatures, lm.indexInfos) - t.objectFlags |= ObjectFlagsUnresolvedMembers - for _, baseType := range lm.baseTypes[:lm.inheritedCount] { - members = c.addInheritedMembers(members, c.getPropertiesOfType(baseType)) - } - } - if lm.state != lazyMembersReady { - lm.state = lazyMembersMaterialized - lm.members = members - return + for _, baseType := range lm.baseTypes { + members = c.addInheritedMembers(members, c.getPropertiesOfType(baseType)) } - t.objectFlags &^= ObjectFlagsUnresolvedMembers c.setStructuredTypeMembers(t, members, lm.callSignatures, lm.constructSignatures, lm.indexInfos) delete(c.lazyMemberTables, t) } @@ -19778,41 +19701,27 @@ func (c *Checker) getMemberOfStructuredType(t *Type, name string) *ast.Symbol { } func (c *Checker) getMemberOfUnresolvedStructuredType(t *Type, name string) *ast.Symbol { - if symbol, ok := c.lookupMemberLazily(t, name); ok { - return symbol + if !isReservedMemberName(name) { + if lm := c.getReadyLazyMemberTable(t); lm != nil { + return c.lookupLazyMember(t, lm, name) + } } return c.resolveStructuredTypeMembers(t).members[name] } -// lookupMemberLazily returns what resolveStructuredTypeMembers(t).members[name] -// would hold, without building t's member table. ok is false when the lazy -// path can't answer and the caller must resolve members normally. -func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, ok bool) { - if isReservedMemberName(name) { - return nil, false - } - lm := c.getLazyMemberTable(t) - if lm == nil || lm.state == lazyMembersResolvingDeclared { - return nil, false - } +// lookupLazyMember returns what resolveStructuredTypeMembers(t).members[name] +// would hold: the declared member, or else the property inherited from the +// first base type that has one, where a base may only fill a missing or +// non-value entry (addInheritedMembers). +func (c *Checker) lookupLazyMember(t *Type, lm *lazyMemberTable, name string) *ast.Symbol { if symbol, ok := lm.found[name]; ok { - return symbol, true + return symbol } - // Mirrors resolveObjectTypeMembers: declared named members first, then - // inherited properties, where a base may only fill a missing or non-value - // entry (addInheritedMembers). - resolved := c.resolveDeclaredMembers(t.Target()) var result *ast.Symbol - if decl := resolved.declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { + if decl := c.resolveDeclaredMembers(t.Target()).declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { result = c.getLazyDeclaredMember(lm, decl, name) } - baseTypes := lm.baseTypes[:lm.inheritedCount] - if lm.state != lazyMembersReady && len(resolved.declaredMembers) == 0 { - // While base types are being resolved, resolveObjectTypeMembers only - // exposes inherited members through the table of declared members. - baseTypes = nil - } - for _, baseType := range baseTypes { + for _, baseType := range lm.baseTypes { if result != nil && result.Flags&ast.SymbolFlagsValue != 0 { break } @@ -19820,10 +19729,8 @@ func (c *Checker) lookupMemberLazily(t *Type, name string) (symbol *ast.Symbol, result = prop } } - if lm.state == lazyMembersReady { - lm.found[name] = result - } - return result, true + lm.found[name] = result + return result } // getNamedPropertyOfType finds name among getPropertiesOfType(t). From d40b4bd485be42751b039725d3849c7c9071ae11 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Tue, 29 Sep 2026 03:14:28 -0400 Subject: [PATCH 7/9] Simplify the lazy member lookup Inherited properties are looked up with getPropertyOfTypeEx instead of a separate helper, lookups are no longer memoized (the memo cost more memory than it saved, with no change in check time), and a few one-use helpers are inlined. Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 93 +++++++-------------------------- 1 file changed, 20 insertions(+), 73 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 3829bf75ff247..92b96c2d314eb 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19543,25 +19543,10 @@ type lazyMemberTable struct { indexInfos []*IndexInfo baseTypes []*Type // instantiated base types declared map[string]*ast.Symbol - found map[string]*ast.Symbol // memoized lookups; nil means no such member } -// isUnresolvedInstantiatedReference reports whether t is a reference to a -// generic class or interface (other than the declaration itself) whose members -// haven't been resolved. -func isUnresolvedInstantiatedReference(t *Type) bool { - if t.flags&TypeFlagsObject == 0 || !mayHaveLazyMembers(t) { - return false - } - source := t.Target() - if source == nil || source == t || source.objectFlags&ObjectFlagsClassOrInterface == 0 || source.objectFlags&ObjectFlagsTuple != 0 { - return false - } - return t.symbol == nil || t.symbol.Flags&ast.SymbolFlagsValueModule == 0 -} - -// mayHaveLazyMembers is a quick check for isUnresolvedInstantiatedReference -// for hot paths, as most types they see have resolved members. +// mayHaveLazyMembers is a quick check for hot paths, as most types they see +// have resolved members. func mayHaveLazyMembers(t *Type) bool { return t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) == ObjectFlagsReference } @@ -19586,7 +19571,11 @@ func (c *Checker) getReadyLazyMemberTable(t *Type) *lazyMemberTable { } func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { - if !isUnresolvedInstantiatedReference(t) { + // Only references to generic classes and interfaces, other than the + // declaration itself, have lazy member tables. + source := t.Target() + if t.flags&TypeFlagsObject == 0 || source == nil || source == t || source.objectFlags&ObjectFlagsClassOrInterface == 0 || + source.objectFlags&ObjectFlagsTuple != 0 || t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsValueModule != 0 { return nil } lm := c.lazyMemberTables[t] @@ -19599,7 +19588,6 @@ func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { mapper: newTypeMapper(typeParameters, typeArguments), typeArguments: typeArguments, declared: map[string]*ast.Symbol{}, - found: map[string]*ast.Symbol{}, } c.lazyMemberTables[t] = lm c.prepareLazyMembers(t, lm) @@ -19656,7 +19644,7 @@ func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { members = make(ast.SymbolTable, len(resolved.declaredMembers)) for id, symbol := range resolved.declaredMembers { if c.isNamedMember(symbol, id) { - members[id] = c.instantiateLazyDeclaredMember(lm, symbol, id) + members[id] = c.getLazyDeclaredMember(lm, symbol, id) } } } @@ -19672,25 +19660,15 @@ func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { result := lm.declared[name] if result == nil { - result = c.instantiateLazyDeclaredMember(lm, symbol, name) + result = symbol + if _, unaffected := slices.BinarySearch(lm.unaffected, name); !unaffected { + result = c.newInstantiatedSymbol(symbol, lm.mapper) + } lm.declared[name] = result } return result } -// instantiateLazyDeclaredMember is getLazyDeclaredMember without memoizing -// the result, for resolving the members in full, after which the table is -// discarded. -func (c *Checker) instantiateLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { - if result := lm.declared[name]; result != nil { - return result - } - if _, unaffected := slices.BinarySearch(lm.unaffected, name); unaffected { - return symbol - } - return c.newInstantiatedSymbol(symbol, lm.mapper) -} - // getMemberOfStructuredType returns c.resolveStructuredTypeMembers(t).members[name] // without resolving the members of t if they are resolved lazily. func (c *Checker) getMemberOfStructuredType(t *Type, name string) *ast.Symbol { @@ -19701,22 +19679,13 @@ func (c *Checker) getMemberOfStructuredType(t *Type, name string) *ast.Symbol { } func (c *Checker) getMemberOfUnresolvedStructuredType(t *Type, name string) *ast.Symbol { - if !isReservedMemberName(name) { - if lm := c.getReadyLazyMemberTable(t); lm != nil { - return c.lookupLazyMember(t, lm, name) - } - } - return c.resolveStructuredTypeMembers(t).members[name] -} - -// lookupLazyMember returns what resolveStructuredTypeMembers(t).members[name] -// would hold: the declared member, or else the property inherited from the -// first base type that has one, where a base may only fill a missing or -// non-value entry (addInheritedMembers). -func (c *Checker) lookupLazyMember(t *Type, lm *lazyMemberTable, name string) *ast.Symbol { - if symbol, ok := lm.found[name]; ok { - return symbol + lm := c.getReadyLazyMemberTable(t) + if lm == nil || isReservedMemberName(name) { + return c.resolveStructuredTypeMembers(t).members[name] } + // As in resolveObjectTypeMembers: the declared member, or else the property + // inherited from the first base type that has one, where a base may only + // fill a missing or non-value entry (addInheritedMembers). var result *ast.Symbol if decl := c.resolveDeclaredMembers(t.Target()).declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { result = c.getLazyDeclaredMember(lm, decl, name) @@ -19725,31 +19694,13 @@ func (c *Checker) lookupLazyMember(t *Type, lm *lazyMemberTable, name string) *a if result != nil && result.Flags&ast.SymbolFlagsValue != 0 { break } - if prop := c.getNamedPropertyOfType(baseType, name); prop != nil && isInheritableProperty(prop) { + if prop := c.getPropertyOfTypeEx(baseType, name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/); prop != nil && !isStaticPrivateIdentifierProperty(prop) { result = prop } } - lm.found[name] = result return result } -// getNamedPropertyOfType finds name among getPropertiesOfType(t). -func (c *Checker) getNamedPropertyOfType(t *Type, name string) *ast.Symbol { - reduced := c.getReducedApparentType(t) - if reduced.flags&TypeFlagsObject != 0 { - if symbol := c.getMemberOfStructuredType(reduced, name); symbol != nil && c.isNamedMember(symbol, name) { - return symbol - } - return nil - } - for _, prop := range c.getPropertiesOfType(t) { - if prop.Name == name { - return prop - } - } - return nil -} - // everyPropertyOfStructuredType is core.Every(c.resolveStructuredTypeMembers(t).properties, f), // without resolving the members of t if they are resolved lazily. f may see a // declared member instead of its instantiation, which has the same flags. @@ -19783,7 +19734,7 @@ func (c *Checker) everyLazyProperty(t *Type, lm *lazyMemberTable, seen *collecti continue } for _, prop := range c.getPropertiesOfType(baseType) { - if isInheritableProperty(prop) && seen.AddIfAbsent(prop.Name) && !f(prop) { + if !isStaticPrivateIdentifierProperty(prop) && seen.AddIfAbsent(prop.Name) && !f(prop) { return false } } @@ -19791,10 +19742,6 @@ func (c *Checker) everyLazyProperty(t *Type, lm *lazyMemberTable, seen *collecti return true } -func isInheritableProperty(prop *ast.Symbol) bool { - return !isStaticPrivateIdentifierProperty(prop) -} - func (c *Checker) getBaseTypes(t *Type) []*Type { if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 { return nil From a5a5da94de80f2f0a602c01d5cfebf04a9e9e926 Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Tue, 29 Sep 2026 03:34:24 -0400 Subject: [PATCH 8/9] Consolidate the tests into one One test covering what the two did that the existing suite doesn't: member lookups with redeclared and private members, base types whose shape depends on the instantiation (weak types, bind, string index signatures, merged signatures), an interface extending a type parameter, a recursive base type and function narrowing. Baselines are generated on unmodified main. Co-Authored-By: Claude Opus 5.5 --- ...nstantiatedReferenceLazyMembers.errors.txt | 112 +++++++ .../instantiatedReferenceLazyMembers.symbols | 259 +++++++++++++++++ .../instantiatedReferenceLazyMembers.types | 193 ++++++++++++ ...stantiatedReferenceMemberLookup.errors.txt | 108 ------- .../instantiatedReferenceMemberLookup.symbols | 275 ------------------ .../instantiatedReferenceMemberLookup.types | 245 ---------------- ...stantiatedReferenceShapeQueries.errors.txt | 109 ------- .../instantiatedReferenceShapeQueries.symbols | 215 -------------- .../instantiatedReferenceShapeQueries.types | 157 ---------- .../instantiatedReferenceLazyMembers.ts | 74 +++++ .../instantiatedReferenceMemberLookup.ts | 85 ------ .../instantiatedReferenceShapeQueries.ts | 71 ----- 12 files changed, 638 insertions(+), 1265 deletions(-) create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.errors.txt create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.symbols create mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.types delete mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt delete mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols delete mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types delete mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt delete mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols delete mode 100644 tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types create mode 100644 tsc/testdata/tests/cases/compiler/instantiatedReferenceLazyMembers.ts delete mode 100644 tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts delete mode 100644 tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.errors.txt b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.errors.txt new file mode 100644 index 0000000000000..88d489ddfbe1a --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.errors.txt @@ -0,0 +1,112 @@ +instantiatedReferenceLazyMembers.ts(14,9): error TS2339: Property 'missing' does not exist on type 'Derived'. +instantiatedReferenceLazyMembers.ts(22,5): error TS2341: Property 'secret' is private and only accessible within class 'Box'. +instantiatedReferenceLazyMembers.ts(31,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<"b">'. +instantiatedReferenceLazyMembers.ts(32,7): error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'Weak<"a">'. +instantiatedReferenceLazyMembers.ts(37,9): error TS2339: Property 'bind' does not exist on type 'Fn<"b">'. +instantiatedReferenceLazyMembers.ts(45,7): error TS2322: Type 'Dict<"b">' is not assignable to type '{ [key: string]: number; }'. + 'string' index signatures are incompatible. + Type 'unknown' is not assignable to type 'number'. +instantiatedReferenceLazyMembers.ts(47,11): error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. + 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'. +instantiatedReferenceLazyMembers.ts(48,7): error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'. +instantiatedReferenceLazyMembers.ts(54,7): error TS2322: Type '(x: U, y: string) => U' is not assignable to type 'number'. +instantiatedReferenceLazyMembers.ts(56,7): error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'. + + +==== instantiatedReferenceLazyMembers.ts (10 errors) ==== + // Member lookups and shape queries on instantiated class and interface + // references, including base types whose shape depends on the instantiation. + + interface Base { + value: T; + shared: string; + } + interface Derived extends Base { + shared: "derived"; + } + declare const derived: Derived; + const derived1 = derived.value; + const derived2 = derived.shared; + derived.missing; + ~~~~~~~ +!!! error TS2339: Property 'missing' does not exist on type 'Derived'. + + class Box { + private secret!: T; + contents!: T; + } + declare const box: Box; + const box1 = box.contents; + box.secret; + ~~~~~~ +!!! error TS2341: Property 'secret' is private and only accessible within class 'Box'. + + // Tagged<"b"> reduces to never, Tagged<"a"> doesn't. + type Tagged = { kind: T } & { kind: "a" }; + declare const other: { other: number }; + + interface Weak extends Tagged { + opt?: number; + } + const weak1: Weak<"b"> = other; + ~~~~~ +!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<"b">'. + const weak2: Weak<"a"> = other; + ~~~~~ +!!! error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'Weak<"a">'. +!!! related TS2728 instantiatedReferenceLazyMembers.ts:25:20: 'kind' is declared here. + + type Callable = { (): void; kind: T } & { kind: "a" }; + interface Fn extends Callable {} + declare const neverFn: Fn<"b">; + neverFn.bind; + ~~~~ +!!! error TS2339: Property 'bind' does not exist on type 'Fn<"b">'. + declare const fn: Fn<"a">; + const fn1 = fn.bind; + + interface Dict extends Tagged { + [key: string]: unknown; + } + declare const dict: Dict<"b">; + const dict1: { [key: string]: number } = dict; + ~~~~~ +!!! error TS2322: Type 'Dict<"b">' is not assignable to type '{ [key: string]: number; }'. +!!! error TS2322: 'string' index signatures are incompatible. +!!! error TS2322: Type 'unknown' is not assignable to type 'number'. + + interface Wrap extends T {} + ~~~~ +!!! error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. +!!! error TS2430: 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'. + const wrap1: Wrap<{ a?: string; b: number }> = other; + ~~~~~ +!!! error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'. +!!! related TS2728 instantiatedReferenceLazyMembers.ts:48:33: 'b' is declared here. + + type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; + interface Overloads extends Two {} + declare function pipe(f: (...args: A) => B): (...args: A) => B; + declare const merged: Overloads; + const piped1: number = pipe(merged); + ~~~~~~ +!!! error TS2322: Type '(x: U, y: string) => U' is not assignable to type 'number'. + declare const distinct: Overloads; + const piped2: number = pipe(distinct); + ~~~~~~ +!!! error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'. + + interface Tree extends Array> { + value: T; + } + declare const tree: Tree; + const tree1 = tree.map(child => child.value); + + interface CallableBox { + (): T; + } + declare const boxOrCallable: CallableBox | Box; + if (typeof boxOrCallable === "function") { + boxOrCallable; + } + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.symbols b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.symbols new file mode 100644 index 0000000000000..0993fb5c79dd3 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.symbols @@ -0,0 +1,259 @@ +//// [tests/cases/compiler/instantiatedReferenceLazyMembers.ts] //// + +=== instantiatedReferenceLazyMembers.ts === +// Member lookups and shape queries on instantiated class and interface +// references, including base types whose shape depends on the instantiation. + +interface Base { +>Base : Symbol(Base, Decl(instantiatedReferenceLazyMembers.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 3, 15)) + + value: T; +>value : Symbol(Base.value, Decl(instantiatedReferenceLazyMembers.ts, 3, 19)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 3, 15)) + + shared: string; +>shared : Symbol(Base.shared, Decl(instantiatedReferenceLazyMembers.ts, 4, 13)) +} +interface Derived extends Base { +>Derived : Symbol(Derived, Decl(instantiatedReferenceLazyMembers.ts, 6, 1)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 7, 18)) +>Base : Symbol(Base, Decl(instantiatedReferenceLazyMembers.ts, 0, 0)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 7, 18)) + + shared: "derived"; +>shared : Symbol(Derived.shared, Decl(instantiatedReferenceLazyMembers.ts, 7, 40)) +} +declare const derived: Derived; +>derived : Symbol(derived, Decl(instantiatedReferenceLazyMembers.ts, 10, 13)) +>Derived : Symbol(Derived, Decl(instantiatedReferenceLazyMembers.ts, 6, 1)) + +const derived1 = derived.value; +>derived1 : Symbol(derived1, Decl(instantiatedReferenceLazyMembers.ts, 11, 5)) +>derived.value : Symbol(Base.value, Decl(instantiatedReferenceLazyMembers.ts, 3, 19)) +>derived : Symbol(derived, Decl(instantiatedReferenceLazyMembers.ts, 10, 13)) +>value : Symbol(Base.value, Decl(instantiatedReferenceLazyMembers.ts, 3, 19)) + +const derived2 = derived.shared; +>derived2 : Symbol(derived2, Decl(instantiatedReferenceLazyMembers.ts, 12, 5)) +>derived.shared : Symbol(Derived.shared, Decl(instantiatedReferenceLazyMembers.ts, 7, 40)) +>derived : Symbol(derived, Decl(instantiatedReferenceLazyMembers.ts, 10, 13)) +>shared : Symbol(Derived.shared, Decl(instantiatedReferenceLazyMembers.ts, 7, 40)) + +derived.missing; +>derived : Symbol(derived, Decl(instantiatedReferenceLazyMembers.ts, 10, 13)) + +class Box { +>Box : Symbol(Box, Decl(instantiatedReferenceLazyMembers.ts, 13, 16)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 15, 10)) + + private secret!: T; +>secret : Symbol(Box.secret, Decl(instantiatedReferenceLazyMembers.ts, 15, 14)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 15, 10)) + + contents!: T; +>contents : Symbol(Box.contents, Decl(instantiatedReferenceLazyMembers.ts, 16, 23)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 15, 10)) +} +declare const box: Box; +>box : Symbol(box, Decl(instantiatedReferenceLazyMembers.ts, 19, 13)) +>Box : Symbol(Box, Decl(instantiatedReferenceLazyMembers.ts, 13, 16)) + +const box1 = box.contents; +>box1 : Symbol(box1, Decl(instantiatedReferenceLazyMembers.ts, 20, 5)) +>box.contents : Symbol(Box.contents, Decl(instantiatedReferenceLazyMembers.ts, 16, 23)) +>box : Symbol(box, Decl(instantiatedReferenceLazyMembers.ts, 19, 13)) +>contents : Symbol(Box.contents, Decl(instantiatedReferenceLazyMembers.ts, 16, 23)) + +box.secret; +>box.secret : Symbol(Box.secret, Decl(instantiatedReferenceLazyMembers.ts, 15, 14)) +>box : Symbol(box, Decl(instantiatedReferenceLazyMembers.ts, 19, 13)) +>secret : Symbol(Box.secret, Decl(instantiatedReferenceLazyMembers.ts, 15, 14)) + +// Tagged<"b"> reduces to never, Tagged<"a"> doesn't. +type Tagged = { kind: T } & { kind: "a" }; +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceLazyMembers.ts, 21, 11)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 24, 12)) +>kind : Symbol(kind, Decl(instantiatedReferenceLazyMembers.ts, 24, 18)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 24, 12)) +>kind : Symbol(kind, Decl(instantiatedReferenceLazyMembers.ts, 24, 32)) + +declare const other: { other: number }; +>other : Symbol(other, Decl(instantiatedReferenceLazyMembers.ts, 25, 13)) +>other : Symbol(other, Decl(instantiatedReferenceLazyMembers.ts, 25, 22)) + +interface Weak extends Tagged { +>Weak : Symbol(Weak, Decl(instantiatedReferenceLazyMembers.ts, 25, 39)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 27, 15)) +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceLazyMembers.ts, 21, 11)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 27, 15)) + + opt?: number; +>opt : Symbol(Weak.opt, Decl(instantiatedReferenceLazyMembers.ts, 27, 52)) +} +const weak1: Weak<"b"> = other; +>weak1 : Symbol(weak1, Decl(instantiatedReferenceLazyMembers.ts, 30, 5)) +>Weak : Symbol(Weak, Decl(instantiatedReferenceLazyMembers.ts, 25, 39)) +>other : Symbol(other, Decl(instantiatedReferenceLazyMembers.ts, 25, 13)) + +const weak2: Weak<"a"> = other; +>weak2 : Symbol(weak2, Decl(instantiatedReferenceLazyMembers.ts, 31, 5)) +>Weak : Symbol(Weak, Decl(instantiatedReferenceLazyMembers.ts, 25, 39)) +>other : Symbol(other, Decl(instantiatedReferenceLazyMembers.ts, 25, 13)) + +type Callable = { (): void; kind: T } & { kind: "a" }; +>Callable : Symbol(Callable, Decl(instantiatedReferenceLazyMembers.ts, 31, 31)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 33, 14)) +>kind : Symbol(kind, Decl(instantiatedReferenceLazyMembers.ts, 33, 30)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 33, 14)) +>kind : Symbol(kind, Decl(instantiatedReferenceLazyMembers.ts, 33, 44)) + +interface Fn extends Callable {} +>Fn : Symbol(Fn, Decl(instantiatedReferenceLazyMembers.ts, 33, 57)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 34, 13)) +>Callable : Symbol(Callable, Decl(instantiatedReferenceLazyMembers.ts, 31, 31)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 34, 13)) + +declare const neverFn: Fn<"b">; +>neverFn : Symbol(neverFn, Decl(instantiatedReferenceLazyMembers.ts, 35, 13)) +>Fn : Symbol(Fn, Decl(instantiatedReferenceLazyMembers.ts, 33, 57)) + +neverFn.bind; +>neverFn : Symbol(neverFn, Decl(instantiatedReferenceLazyMembers.ts, 35, 13)) + +declare const fn: Fn<"a">; +>fn : Symbol(fn, Decl(instantiatedReferenceLazyMembers.ts, 37, 13)) +>Fn : Symbol(Fn, Decl(instantiatedReferenceLazyMembers.ts, 33, 57)) + +const fn1 = fn.bind; +>fn1 : Symbol(fn1, Decl(instantiatedReferenceLazyMembers.ts, 38, 5)) +>fn.bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) +>fn : Symbol(fn, Decl(instantiatedReferenceLazyMembers.ts, 37, 13)) +>bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) + +interface Dict extends Tagged { +>Dict : Symbol(Dict, Decl(instantiatedReferenceLazyMembers.ts, 38, 20)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 40, 15)) +>Tagged : Symbol(Tagged, Decl(instantiatedReferenceLazyMembers.ts, 21, 11)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 40, 15)) + + [key: string]: unknown; +>key : Symbol(key, Decl(instantiatedReferenceLazyMembers.ts, 41, 5)) +} +declare const dict: Dict<"b">; +>dict : Symbol(dict, Decl(instantiatedReferenceLazyMembers.ts, 43, 13)) +>Dict : Symbol(Dict, Decl(instantiatedReferenceLazyMembers.ts, 38, 20)) + +const dict1: { [key: string]: number } = dict; +>dict1 : Symbol(dict1, Decl(instantiatedReferenceLazyMembers.ts, 44, 5)) +>key : Symbol(key, Decl(instantiatedReferenceLazyMembers.ts, 44, 16)) +>dict : Symbol(dict, Decl(instantiatedReferenceLazyMembers.ts, 43, 13)) + +interface Wrap extends T {} +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceLazyMembers.ts, 44, 46)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 46, 15)) +>a : Symbol(a, Decl(instantiatedReferenceLazyMembers.ts, 46, 26)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 46, 15)) + +const wrap1: Wrap<{ a?: string; b: number }> = other; +>wrap1 : Symbol(wrap1, Decl(instantiatedReferenceLazyMembers.ts, 47, 5)) +>Wrap : Symbol(Wrap, Decl(instantiatedReferenceLazyMembers.ts, 44, 46)) +>a : Symbol(a, Decl(instantiatedReferenceLazyMembers.ts, 47, 19)) +>b : Symbol(b, Decl(instantiatedReferenceLazyMembers.ts, 47, 31)) +>other : Symbol(other, Decl(instantiatedReferenceLazyMembers.ts, 25, 13)) + +type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; +>Two : Symbol(Two, Decl(instantiatedReferenceLazyMembers.ts, 47, 53)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 49, 9)) +>U : Symbol(U, Decl(instantiatedReferenceLazyMembers.ts, 49, 17)) +>x : Symbol(x, Decl(instantiatedReferenceLazyMembers.ts, 49, 20)) +>U : Symbol(U, Decl(instantiatedReferenceLazyMembers.ts, 49, 17)) +>y : Symbol(y, Decl(instantiatedReferenceLazyMembers.ts, 49, 25)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 49, 9)) +>U : Symbol(U, Decl(instantiatedReferenceLazyMembers.ts, 49, 17)) +>U : Symbol(U, Decl(instantiatedReferenceLazyMembers.ts, 49, 42)) +>x : Symbol(x, Decl(instantiatedReferenceLazyMembers.ts, 49, 45)) +>U : Symbol(U, Decl(instantiatedReferenceLazyMembers.ts, 49, 42)) +>y : Symbol(y, Decl(instantiatedReferenceLazyMembers.ts, 49, 50)) +>U : Symbol(U, Decl(instantiatedReferenceLazyMembers.ts, 49, 42)) + +interface Overloads extends Two {} +>Overloads : Symbol(Overloads, Decl(instantiatedReferenceLazyMembers.ts, 49, 67)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 50, 20)) +>Two : Symbol(Two, Decl(instantiatedReferenceLazyMembers.ts, 47, 53)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 50, 20)) + +declare function pipe(f: (...args: A) => B): (...args: A) => B; +>pipe : Symbol(pipe, Decl(instantiatedReferenceLazyMembers.ts, 50, 40)) +>A : Symbol(A, Decl(instantiatedReferenceLazyMembers.ts, 51, 22)) +>B : Symbol(B, Decl(instantiatedReferenceLazyMembers.ts, 51, 38)) +>f : Symbol(f, Decl(instantiatedReferenceLazyMembers.ts, 51, 42)) +>args : Symbol(args, Decl(instantiatedReferenceLazyMembers.ts, 51, 46)) +>A : Symbol(A, Decl(instantiatedReferenceLazyMembers.ts, 51, 22)) +>B : Symbol(B, Decl(instantiatedReferenceLazyMembers.ts, 51, 38)) +>args : Symbol(args, Decl(instantiatedReferenceLazyMembers.ts, 51, 66)) +>A : Symbol(A, Decl(instantiatedReferenceLazyMembers.ts, 51, 22)) +>B : Symbol(B, Decl(instantiatedReferenceLazyMembers.ts, 51, 38)) + +declare const merged: Overloads; +>merged : Symbol(merged, Decl(instantiatedReferenceLazyMembers.ts, 52, 13)) +>Overloads : Symbol(Overloads, Decl(instantiatedReferenceLazyMembers.ts, 49, 67)) + +const piped1: number = pipe(merged); +>piped1 : Symbol(piped1, Decl(instantiatedReferenceLazyMembers.ts, 53, 5)) +>pipe : Symbol(pipe, Decl(instantiatedReferenceLazyMembers.ts, 50, 40)) +>merged : Symbol(merged, Decl(instantiatedReferenceLazyMembers.ts, 52, 13)) + +declare const distinct: Overloads; +>distinct : Symbol(distinct, Decl(instantiatedReferenceLazyMembers.ts, 54, 13)) +>Overloads : Symbol(Overloads, Decl(instantiatedReferenceLazyMembers.ts, 49, 67)) + +const piped2: number = pipe(distinct); +>piped2 : Symbol(piped2, Decl(instantiatedReferenceLazyMembers.ts, 55, 5)) +>pipe : Symbol(pipe, Decl(instantiatedReferenceLazyMembers.ts, 50, 40)) +>distinct : Symbol(distinct, Decl(instantiatedReferenceLazyMembers.ts, 54, 13)) + +interface Tree extends Array> { +>Tree : Symbol(Tree, Decl(instantiatedReferenceLazyMembers.ts, 55, 38)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 57, 15)) +>Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) +>Tree : Symbol(Tree, Decl(instantiatedReferenceLazyMembers.ts, 55, 38)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 57, 15)) + + value: T; +>value : Symbol(Tree.value, Decl(instantiatedReferenceLazyMembers.ts, 57, 42)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 57, 15)) +} +declare const tree: Tree; +>tree : Symbol(tree, Decl(instantiatedReferenceLazyMembers.ts, 60, 13)) +>Tree : Symbol(Tree, Decl(instantiatedReferenceLazyMembers.ts, 55, 38)) + +const tree1 = tree.map(child => child.value); +>tree1 : Symbol(tree1, Decl(instantiatedReferenceLazyMembers.ts, 61, 5)) +>tree.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>tree : Symbol(tree, Decl(instantiatedReferenceLazyMembers.ts, 60, 13)) +>map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) +>child : Symbol(child, Decl(instantiatedReferenceLazyMembers.ts, 61, 23)) +>child.value : Symbol(Tree.value, Decl(instantiatedReferenceLazyMembers.ts, 57, 42)) +>child : Symbol(child, Decl(instantiatedReferenceLazyMembers.ts, 61, 23)) +>value : Symbol(Tree.value, Decl(instantiatedReferenceLazyMembers.ts, 57, 42)) + +interface CallableBox { +>CallableBox : Symbol(CallableBox, Decl(instantiatedReferenceLazyMembers.ts, 61, 45)) +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 63, 22)) + + (): T; +>T : Symbol(T, Decl(instantiatedReferenceLazyMembers.ts, 63, 22)) +} +declare const boxOrCallable: CallableBox | Box; +>boxOrCallable : Symbol(boxOrCallable, Decl(instantiatedReferenceLazyMembers.ts, 66, 13)) +>CallableBox : Symbol(CallableBox, Decl(instantiatedReferenceLazyMembers.ts, 61, 45)) +>Box : Symbol(Box, Decl(instantiatedReferenceLazyMembers.ts, 13, 16)) + +if (typeof boxOrCallable === "function") { +>boxOrCallable : Symbol(boxOrCallable, Decl(instantiatedReferenceLazyMembers.ts, 66, 13)) + + boxOrCallable; +>boxOrCallable : Symbol(boxOrCallable, Decl(instantiatedReferenceLazyMembers.ts, 66, 13)) +} + diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.types b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.types new file mode 100644 index 0000000000000..970b6c79de36c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceLazyMembers.types @@ -0,0 +1,193 @@ +//// [tests/cases/compiler/instantiatedReferenceLazyMembers.ts] //// + +=== instantiatedReferenceLazyMembers.ts === +// Member lookups and shape queries on instantiated class and interface +// references, including base types whose shape depends on the instantiation. + +interface Base { + value: T; +>value : T + + shared: string; +>shared : string +} +interface Derived extends Base { + shared: "derived"; +>shared : "derived" +} +declare const derived: Derived; +>derived : Derived + +const derived1 = derived.value; +>derived1 : number[] +>derived.value : number[] +>derived : Derived +>value : number[] + +const derived2 = derived.shared; +>derived2 : "derived" +>derived.shared : "derived" +>derived : Derived +>shared : "derived" + +derived.missing; +>derived.missing : any +>derived : Derived +>missing : any + +class Box { +>Box : Box + + private secret!: T; +>secret : T + + contents!: T; +>contents : T +} +declare const box: Box; +>box : Box + +const box1 = box.contents; +>box1 : string +>box.contents : string +>box : Box +>contents : string + +box.secret; +>box.secret : string +>box : Box +>secret : string + +// Tagged<"b"> reduces to never, Tagged<"a"> doesn't. +type Tagged = { kind: T } & { kind: "a" }; +>Tagged : Tagged +>kind : T +>kind : "a" + +declare const other: { other: number }; +>other : { other: number; } +>other : number + +interface Weak extends Tagged { + opt?: number; +>opt : number | undefined +} +const weak1: Weak<"b"> = other; +>weak1 : Weak<"b"> +>other : { other: number; } + +const weak2: Weak<"a"> = other; +>weak2 : Weak<"a"> +>other : { other: number; } + +type Callable = { (): void; kind: T } & { kind: "a" }; +>Callable : Callable +>kind : T +>kind : "a" + +interface Fn extends Callable {} +declare const neverFn: Fn<"b">; +>neverFn : Fn<"b"> + +neverFn.bind; +>neverFn.bind : any +>neverFn : Fn<"b"> +>bind : any + +declare const fn: Fn<"a">; +>fn : Fn<"a"> + +const fn1 = fn.bind; +>fn1 : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>fn.bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } +>fn : Fn<"a"> +>bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } + +interface Dict extends Tagged { + [key: string]: unknown; +>key : string +} +declare const dict: Dict<"b">; +>dict : Dict<"b"> + +const dict1: { [key: string]: number } = dict; +>dict1 : { [key: string]: number; } +>key : string +>dict : Dict<"b"> + +interface Wrap extends T {} +>a : string | undefined + +const wrap1: Wrap<{ a?: string; b: number }> = other; +>wrap1 : Wrap<{ a?: string; b: number; }> +>a : string | undefined +>b : number +>other : { other: number; } + +type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; +>Two : Two +>x : U +>y : T +>x : U +>y : string + +interface Overloads extends Two {} +declare function pipe(f: (...args: A) => B): (...args: A) => B; +>pipe : (f: (...args: A) => B) => (...args: A) => B +>f : (...args: A) => B +>args : A +>args : A + +declare const merged: Overloads; +>merged : Overloads + +const piped1: number = pipe(merged); +>piped1 : number +>pipe(merged) : (x: U, y: string) => U +>pipe : (f: (...args: A) => B) => (...args: A) => B +>merged : Overloads + +declare const distinct: Overloads; +>distinct : Overloads + +const piped2: number = pipe(distinct); +>piped2 : number +>pipe(distinct) : (x: unknown, y: string) => unknown +>pipe : (f: (...args: A) => B) => (...args: A) => B +>distinct : Overloads + +interface Tree extends Array> { + value: T; +>value : T +} +declare const tree: Tree; +>tree : Tree + +const tree1 = tree.map(child => child.value); +>tree1 : string[] +>tree.map(child => child.value) : string[] +>tree.map : (callbackfn: (value: Tree, index: number, array: Tree[]) => U, thisArg?: any) => U[] +>tree : Tree +>map : (callbackfn: (value: Tree, index: number, array: Tree[]) => U, thisArg?: any) => U[] +>child => child.value : (child: Tree) => string +>child : Tree +>child.value : string +>child : Tree +>value : string + +interface CallableBox { + (): T; +} +declare const boxOrCallable: CallableBox | Box; +>boxOrCallable : Box | CallableBox + +if (typeof boxOrCallable === "function") { +>typeof boxOrCallable === "function" : boolean +>typeof boxOrCallable : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" +>boxOrCallable : Box | CallableBox +>"function" : "function" + + boxOrCallable; +>boxOrCallable : CallableBox +} + diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt deleted file mode 100644 index 8bbd039be8d19..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.errors.txt +++ /dev/null @@ -1,108 +0,0 @@ -instantiatedReferenceMemberLookup.ts(23,3): error TS2339: Property 'missing' does not exist on type 'Derived'. -instantiatedReferenceMemberLookup.ts(43,5): error TS2341: Property 'secret' is private and only accessible within class 'Box'. -instantiatedReferenceMemberLookup.ts(44,5): error TS2445: Property 'guarded' is protected and only accessible within class 'Box' and its subclasses. -instantiatedReferenceMemberLookup.ts(53,9): error TS2339: Property 'bind' does not exist on type 'Fn<"b">'. -instantiatedReferenceMemberLookup.ts(54,9): error TS2339: Property 'kind' does not exist on type 'Fn<"b">'. -instantiatedReferenceMemberLookup.ts(69,11): error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. - 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: string; }'. -instantiatedReferenceMemberLookup.ts(73,6): error TS2339: Property 'c' does not exist on type 'Wrap<{ a: string; b: number; }>'. - - -==== instantiatedReferenceMemberLookup.ts (7 errors) ==== - // Property lookups on instantiated class and interface references: declared - // and inherited members, members hidden by redeclarations, accessibility, - // `this` types, Object and Function members, and base types that change shape - // when instantiated. - - interface Base { - value: T; - method(): T; - shared: string; - } - - interface Derived extends Base { - shared: "derived"; - own: T; - } - - declare const d: Derived; - const d1 = d.value; - const d2 = d.method(); - const d3 = d.shared; - const d4 = d.own; - const d5 = d.toString(); - d.missing; - ~~~~~~~ -!!! error TS2339: Property 'missing' does not exist on type 'Derived'. - - class Box { - private secret!: T; - protected guarded!: T; - contents!: T; - self(): this { - return this; - } - } - - class NumberBox extends Box { - read() { - return this.guarded; - } - } - - declare const box: Box; - const box1 = box.contents; - const box2 = box.self().contents; - box.secret; - ~~~~~~ -!!! error TS2341: Property 'secret' is private and only accessible within class 'Box'. - box.guarded; - ~~~~~~~ -!!! error TS2445: Property 'guarded' is protected and only accessible within class 'Box' and its subclasses. - - declare const numberBox: NumberBox; - const numberBox1 = numberBox.self().read(); - - type Callable = { (): void; kind: T } & { kind: "a" }; - interface Fn extends Callable {} - - declare const neverFn: Fn<"b">; - neverFn.bind; - ~~~~ -!!! error TS2339: Property 'bind' does not exist on type 'Fn<"b">'. - neverFn.kind; - ~~~~ -!!! error TS2339: Property 'kind' does not exist on type 'Fn<"b">'. - - declare const fn: Fn<"a">; - const fn1 = fn.bind; - const fn2 = fn.kind; - - interface Newable { - new (): T; - } - interface NewableBox extends Newable> {} - - declare const newable: NewableBox; - const newable1 = newable.prototype; - const newable2 = new newable().contents; - - interface Wrap extends T {} - ~~~~ -!!! error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. -!!! error TS2430: 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a: string; }'. - - declare const wrap: Wrap<{ a: string; b: number }>; - const wrap1 = wrap.b; - wrap.c; - ~ -!!! error TS2339: Property 'c' does not exist on type 'Wrap<{ a: string; b: number; }>'. - - interface Tree extends Array> { - value: T; - } - - declare const tree: Tree; - const tree1 = tree[0].value; - const tree2 = tree.map(child => child.value); - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols deleted file mode 100644 index 3001786ac5fcd..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.symbols +++ /dev/null @@ -1,275 +0,0 @@ -//// [tests/cases/compiler/instantiatedReferenceMemberLookup.ts] //// - -=== instantiatedReferenceMemberLookup.ts === -// Property lookups on instantiated class and interface references: declared -// and inherited members, members hidden by redeclarations, accessibility, -// `this` types, Object and Function members, and base types that change shape -// when instantiated. - -interface Base { ->Base : Symbol(Base, Decl(instantiatedReferenceMemberLookup.ts, 0, 0)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 5, 15)) - - value: T; ->value : Symbol(Base.value, Decl(instantiatedReferenceMemberLookup.ts, 5, 19)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 5, 15)) - - method(): T; ->method : Symbol(Base.method, Decl(instantiatedReferenceMemberLookup.ts, 6, 13)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 5, 15)) - - shared: string; ->shared : Symbol(Base.shared, Decl(instantiatedReferenceMemberLookup.ts, 7, 16)) -} - -interface Derived extends Base { ->Derived : Symbol(Derived, Decl(instantiatedReferenceMemberLookup.ts, 9, 1)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 11, 18)) ->Base : Symbol(Base, Decl(instantiatedReferenceMemberLookup.ts, 0, 0)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 11, 18)) - - shared: "derived"; ->shared : Symbol(Derived.shared, Decl(instantiatedReferenceMemberLookup.ts, 11, 40)) - - own: T; ->own : Symbol(Derived.own, Decl(instantiatedReferenceMemberLookup.ts, 12, 22)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 11, 18)) -} - -declare const d: Derived; ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) ->Derived : Symbol(Derived, Decl(instantiatedReferenceMemberLookup.ts, 9, 1)) - -const d1 = d.value; ->d1 : Symbol(d1, Decl(instantiatedReferenceMemberLookup.ts, 17, 5)) ->d.value : Symbol(Base.value, Decl(instantiatedReferenceMemberLookup.ts, 5, 19)) ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) ->value : Symbol(Base.value, Decl(instantiatedReferenceMemberLookup.ts, 5, 19)) - -const d2 = d.method(); ->d2 : Symbol(d2, Decl(instantiatedReferenceMemberLookup.ts, 18, 5)) ->d.method : Symbol(Base.method, Decl(instantiatedReferenceMemberLookup.ts, 6, 13)) ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) ->method : Symbol(Base.method, Decl(instantiatedReferenceMemberLookup.ts, 6, 13)) - -const d3 = d.shared; ->d3 : Symbol(d3, Decl(instantiatedReferenceMemberLookup.ts, 19, 5)) ->d.shared : Symbol(Derived.shared, Decl(instantiatedReferenceMemberLookup.ts, 11, 40)) ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) ->shared : Symbol(Derived.shared, Decl(instantiatedReferenceMemberLookup.ts, 11, 40)) - -const d4 = d.own; ->d4 : Symbol(d4, Decl(instantiatedReferenceMemberLookup.ts, 20, 5)) ->d.own : Symbol(Derived.own, Decl(instantiatedReferenceMemberLookup.ts, 12, 22)) ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) ->own : Symbol(Derived.own, Decl(instantiatedReferenceMemberLookup.ts, 12, 22)) - -const d5 = d.toString(); ->d5 : Symbol(d5, Decl(instantiatedReferenceMemberLookup.ts, 21, 5)) ->d.toString : Symbol(Object.toString, Decl(lib.es5.d.ts, --, --)) ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) ->toString : Symbol(Object.toString, Decl(lib.es5.d.ts, --, --)) - -d.missing; ->d : Symbol(d, Decl(instantiatedReferenceMemberLookup.ts, 16, 13)) - -class Box { ->Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) - - private secret!: T; ->secret : Symbol(Box.secret, Decl(instantiatedReferenceMemberLookup.ts, 24, 14)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) - - protected guarded!: T; ->guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) - - contents!: T; ->contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 24, 10)) - - self(): this { ->self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) - - return this; ->this : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) - } -} - -class NumberBox extends Box { ->NumberBox : Symbol(NumberBox, Decl(instantiatedReferenceMemberLookup.ts, 31, 1)) ->Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) - - read() { ->read : Symbol(NumberBox.read, Decl(instantiatedReferenceMemberLookup.ts, 33, 37)) - - return this.guarded; ->this.guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) ->this : Symbol(NumberBox, Decl(instantiatedReferenceMemberLookup.ts, 31, 1)) ->guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) - } -} - -declare const box: Box; ->box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) ->Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) - -const box1 = box.contents; ->box1 : Symbol(box1, Decl(instantiatedReferenceMemberLookup.ts, 40, 5)) ->box.contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) ->box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) ->contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) - -const box2 = box.self().contents; ->box2 : Symbol(box2, Decl(instantiatedReferenceMemberLookup.ts, 41, 5)) ->box.self().contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) ->box.self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) ->box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) ->self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) ->contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) - -box.secret; ->box.secret : Symbol(Box.secret, Decl(instantiatedReferenceMemberLookup.ts, 24, 14)) ->box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) ->secret : Symbol(Box.secret, Decl(instantiatedReferenceMemberLookup.ts, 24, 14)) - -box.guarded; ->box.guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) ->box : Symbol(box, Decl(instantiatedReferenceMemberLookup.ts, 39, 13)) ->guarded : Symbol(Box.guarded, Decl(instantiatedReferenceMemberLookup.ts, 25, 23)) - -declare const numberBox: NumberBox; ->numberBox : Symbol(numberBox, Decl(instantiatedReferenceMemberLookup.ts, 45, 13)) ->NumberBox : Symbol(NumberBox, Decl(instantiatedReferenceMemberLookup.ts, 31, 1)) - -const numberBox1 = numberBox.self().read(); ->numberBox1 : Symbol(numberBox1, Decl(instantiatedReferenceMemberLookup.ts, 46, 5)) ->numberBox.self().read : Symbol(NumberBox.read, Decl(instantiatedReferenceMemberLookup.ts, 33, 37)) ->numberBox.self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) ->numberBox : Symbol(numberBox, Decl(instantiatedReferenceMemberLookup.ts, 45, 13)) ->self : Symbol(Box.self, Decl(instantiatedReferenceMemberLookup.ts, 27, 17)) ->read : Symbol(NumberBox.read, Decl(instantiatedReferenceMemberLookup.ts, 33, 37)) - -type Callable = { (): void; kind: T } & { kind: "a" }; ->Callable : Symbol(Callable, Decl(instantiatedReferenceMemberLookup.ts, 46, 43)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 48, 14)) ->kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 30)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 48, 14)) ->kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 44)) - -interface Fn extends Callable {} ->Fn : Symbol(Fn, Decl(instantiatedReferenceMemberLookup.ts, 48, 57)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 49, 13)) ->Callable : Symbol(Callable, Decl(instantiatedReferenceMemberLookup.ts, 46, 43)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 49, 13)) - -declare const neverFn: Fn<"b">; ->neverFn : Symbol(neverFn, Decl(instantiatedReferenceMemberLookup.ts, 51, 13)) ->Fn : Symbol(Fn, Decl(instantiatedReferenceMemberLookup.ts, 48, 57)) - -neverFn.bind; ->neverFn : Symbol(neverFn, Decl(instantiatedReferenceMemberLookup.ts, 51, 13)) - -neverFn.kind; ->neverFn : Symbol(neverFn, Decl(instantiatedReferenceMemberLookup.ts, 51, 13)) - -declare const fn: Fn<"a">; ->fn : Symbol(fn, Decl(instantiatedReferenceMemberLookup.ts, 55, 13)) ->Fn : Symbol(Fn, Decl(instantiatedReferenceMemberLookup.ts, 48, 57)) - -const fn1 = fn.bind; ->fn1 : Symbol(fn1, Decl(instantiatedReferenceMemberLookup.ts, 56, 5)) ->fn.bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->fn : Symbol(fn, Decl(instantiatedReferenceMemberLookup.ts, 55, 13)) ->bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) - -const fn2 = fn.kind; ->fn2 : Symbol(fn2, Decl(instantiatedReferenceMemberLookup.ts, 57, 5)) ->fn.kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 30), Decl(instantiatedReferenceMemberLookup.ts, 48, 44)) ->fn : Symbol(fn, Decl(instantiatedReferenceMemberLookup.ts, 55, 13)) ->kind : Symbol(kind, Decl(instantiatedReferenceMemberLookup.ts, 48, 30), Decl(instantiatedReferenceMemberLookup.ts, 48, 44)) - -interface Newable { ->Newable : Symbol(Newable, Decl(instantiatedReferenceMemberLookup.ts, 57, 20)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 59, 18)) - - new (): T; ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 59, 18)) -} -interface NewableBox extends Newable> {} ->NewableBox : Symbol(NewableBox, Decl(instantiatedReferenceMemberLookup.ts, 61, 1)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 62, 21)) ->Newable : Symbol(Newable, Decl(instantiatedReferenceMemberLookup.ts, 57, 20)) ->Box : Symbol(Box, Decl(instantiatedReferenceMemberLookup.ts, 22, 10)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 62, 21)) - -declare const newable: NewableBox; ->newable : Symbol(newable, Decl(instantiatedReferenceMemberLookup.ts, 64, 13)) ->NewableBox : Symbol(NewableBox, Decl(instantiatedReferenceMemberLookup.ts, 61, 1)) - -const newable1 = newable.prototype; ->newable1 : Symbol(newable1, Decl(instantiatedReferenceMemberLookup.ts, 65, 5)) ->newable.prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) ->newable : Symbol(newable, Decl(instantiatedReferenceMemberLookup.ts, 64, 13)) ->prototype : Symbol(Function.prototype, Decl(lib.es5.d.ts, --, --)) - -const newable2 = new newable().contents; ->newable2 : Symbol(newable2, Decl(instantiatedReferenceMemberLookup.ts, 66, 5)) ->new newable().contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) ->newable : Symbol(newable, Decl(instantiatedReferenceMemberLookup.ts, 64, 13)) ->contents : Symbol(Box.contents, Decl(instantiatedReferenceMemberLookup.ts, 26, 26)) - -interface Wrap extends T {} ->Wrap : Symbol(Wrap, Decl(instantiatedReferenceMemberLookup.ts, 66, 40)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 68, 15)) ->a : Symbol(a, Decl(instantiatedReferenceMemberLookup.ts, 68, 26)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 68, 15)) - -declare const wrap: Wrap<{ a: string; b: number }>; ->wrap : Symbol(wrap, Decl(instantiatedReferenceMemberLookup.ts, 70, 13)) ->Wrap : Symbol(Wrap, Decl(instantiatedReferenceMemberLookup.ts, 66, 40)) ->a : Symbol(a, Decl(instantiatedReferenceMemberLookup.ts, 70, 26)) ->b : Symbol(b, Decl(instantiatedReferenceMemberLookup.ts, 70, 37)) - -const wrap1 = wrap.b; ->wrap1 : Symbol(wrap1, Decl(instantiatedReferenceMemberLookup.ts, 71, 5)) ->wrap.b : Symbol(b, Decl(instantiatedReferenceMemberLookup.ts, 70, 37)) ->wrap : Symbol(wrap, Decl(instantiatedReferenceMemberLookup.ts, 70, 13)) ->b : Symbol(b, Decl(instantiatedReferenceMemberLookup.ts, 70, 37)) - -wrap.c; ->wrap : Symbol(wrap, Decl(instantiatedReferenceMemberLookup.ts, 70, 13)) - -interface Tree extends Array> { ->Tree : Symbol(Tree, Decl(instantiatedReferenceMemberLookup.ts, 72, 7)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 74, 15)) ->Array : Symbol(Array, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --), Decl(lib.es2015.core.d.ts, --, --), Decl(lib.es2015.iterable.d.ts, --, --), Decl(lib.es2015.symbol.wellknown.d.ts, --, --) ... and 4 more) ->Tree : Symbol(Tree, Decl(instantiatedReferenceMemberLookup.ts, 72, 7)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 74, 15)) - - value: T; ->value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) ->T : Symbol(T, Decl(instantiatedReferenceMemberLookup.ts, 74, 15)) -} - -declare const tree: Tree; ->tree : Symbol(tree, Decl(instantiatedReferenceMemberLookup.ts, 78, 13)) ->Tree : Symbol(Tree, Decl(instantiatedReferenceMemberLookup.ts, 72, 7)) - -const tree1 = tree[0].value; ->tree1 : Symbol(tree1, Decl(instantiatedReferenceMemberLookup.ts, 79, 5)) ->tree[0].value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) ->tree : Symbol(tree, Decl(instantiatedReferenceMemberLookup.ts, 78, 13)) ->value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) - -const tree2 = tree.map(child => child.value); ->tree2 : Symbol(tree2, Decl(instantiatedReferenceMemberLookup.ts, 80, 5)) ->tree.map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) ->tree : Symbol(tree, Decl(instantiatedReferenceMemberLookup.ts, 78, 13)) ->map : Symbol(Array.map, Decl(lib.es5.d.ts, --, --)) ->child : Symbol(child, Decl(instantiatedReferenceMemberLookup.ts, 80, 23)) ->child.value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) ->child : Symbol(child, Decl(instantiatedReferenceMemberLookup.ts, 80, 23)) ->value : Symbol(Tree.value, Decl(instantiatedReferenceMemberLookup.ts, 74, 42)) - diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types deleted file mode 100644 index 46483d5c30262..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceMemberLookup.types +++ /dev/null @@ -1,245 +0,0 @@ -//// [tests/cases/compiler/instantiatedReferenceMemberLookup.ts] //// - -=== instantiatedReferenceMemberLookup.ts === -// Property lookups on instantiated class and interface references: declared -// and inherited members, members hidden by redeclarations, accessibility, -// `this` types, Object and Function members, and base types that change shape -// when instantiated. - -interface Base { - value: T; ->value : T - - method(): T; ->method : () => T - - shared: string; ->shared : string -} - -interface Derived extends Base { - shared: "derived"; ->shared : "derived" - - own: T; ->own : T -} - -declare const d: Derived; ->d : Derived - -const d1 = d.value; ->d1 : number[] ->d.value : number[] ->d : Derived ->value : number[] - -const d2 = d.method(); ->d2 : number[] ->d.method() : number[] ->d.method : () => number[] ->d : Derived ->method : () => number[] - -const d3 = d.shared; ->d3 : "derived" ->d.shared : "derived" ->d : Derived ->shared : "derived" - -const d4 = d.own; ->d4 : number ->d.own : number ->d : Derived ->own : number - -const d5 = d.toString(); ->d5 : string ->d.toString() : string ->d.toString : () => string ->d : Derived ->toString : () => string - -d.missing; ->d.missing : any ->d : Derived ->missing : any - -class Box { ->Box : Box - - private secret!: T; ->secret : T - - protected guarded!: T; ->guarded : T - - contents!: T; ->contents : T - - self(): this { ->self : () => this - - return this; ->this : this - } -} - -class NumberBox extends Box { ->NumberBox : NumberBox ->Box : Box - - read() { ->read : () => number - - return this.guarded; ->this.guarded : number ->this : this ->guarded : number - } -} - -declare const box: Box; ->box : Box - -const box1 = box.contents; ->box1 : string ->box.contents : string ->box : Box ->contents : string - -const box2 = box.self().contents; ->box2 : string ->box.self().contents : string ->box.self() : Box ->box.self : () => Box ->box : Box ->self : () => Box ->contents : string - -box.secret; ->box.secret : string ->box : Box ->secret : string - -box.guarded; ->box.guarded : string ->box : Box ->guarded : string - -declare const numberBox: NumberBox; ->numberBox : NumberBox - -const numberBox1 = numberBox.self().read(); ->numberBox1 : number ->numberBox.self().read() : number ->numberBox.self().read : () => number ->numberBox.self() : NumberBox ->numberBox.self : () => NumberBox ->numberBox : NumberBox ->self : () => NumberBox ->read : () => number - -type Callable = { (): void; kind: T } & { kind: "a" }; ->Callable : Callable ->kind : T ->kind : "a" - -interface Fn extends Callable {} - -declare const neverFn: Fn<"b">; ->neverFn : Fn<"b"> - -neverFn.bind; ->neverFn.bind : any ->neverFn : Fn<"b"> ->bind : any - -neverFn.kind; ->neverFn.kind : any ->neverFn : Fn<"b"> ->kind : any - -declare const fn: Fn<"a">; ->fn : Fn<"a"> - -const fn1 = fn.bind; ->fn1 : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } ->fn.bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } ->fn : Fn<"a"> ->bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } - -const fn2 = fn.kind; ->fn2 : "a" ->fn.kind : "a" ->fn : Fn<"a"> ->kind : "a" - -interface Newable { - new (): T; -} -interface NewableBox extends Newable> {} - -declare const newable: NewableBox; ->newable : NewableBox - -const newable1 = newable.prototype; ->newable1 : any ->newable.prototype : any ->newable : NewableBox ->prototype : any - -const newable2 = new newable().contents; ->newable2 : string ->new newable().contents : string ->new newable() : Box ->newable : NewableBox ->contents : string - -interface Wrap extends T {} ->a : string - -declare const wrap: Wrap<{ a: string; b: number }>; ->wrap : Wrap<{ a: string; b: number; }> ->a : string ->b : number - -const wrap1 = wrap.b; ->wrap1 : number ->wrap.b : number ->wrap : Wrap<{ a: string; b: number; }> ->b : number - -wrap.c; ->wrap.c : any ->wrap : Wrap<{ a: string; b: number; }> ->c : any - -interface Tree extends Array> { - value: T; ->value : T -} - -declare const tree: Tree; ->tree : Tree - -const tree1 = tree[0].value; ->tree1 : string ->tree[0].value : string ->tree[0] : Tree ->tree : Tree ->0 : 0 ->value : string - -const tree2 = tree.map(child => child.value); ->tree2 : string[] ->tree.map(child => child.value) : string[] ->tree.map : (callbackfn: (value: Tree, index: number, array: Tree[]) => U, thisArg?: any) => U[] ->tree : Tree ->map : (callbackfn: (value: Tree, index: number, array: Tree[]) => U, thisArg?: any) => U[] ->child => child.value : (child: Tree) => string ->child : Tree ->child.value : string ->child : Tree ->value : string - diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt deleted file mode 100644 index d94bb38443cfb..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.errors.txt +++ /dev/null @@ -1,109 +0,0 @@ -instantiatedReferenceShapeQueries.ts(12,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<"b">'. -instantiatedReferenceShapeQueries.ts(13,7): error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'Weak<"a">'. -instantiatedReferenceShapeQueries.ts(22,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptional'. -instantiatedReferenceShapeQueries.ts(23,7): error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptionalDerived'. -instantiatedReferenceShapeQueries.ts(27,41): error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type 'Empty<"a">'. -instantiatedReferenceShapeQueries.ts(35,7): error TS2322: Type '(x: U, y: string) => U' is not assignable to type 'number'. -instantiatedReferenceShapeQueries.ts(38,7): error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'. -instantiatedReferenceShapeQueries.ts(46,7): error TS2322: Type 'Dict<"b">' is not assignable to type '{ [key: string]: number; }'. - 'string' index signatures are incompatible. - Type 'unknown' is not assignable to type 'number'. -instantiatedReferenceShapeQueries.ts(48,11): error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. - 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'. -instantiatedReferenceShapeQueries.ts(50,7): error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'. - - -==== instantiatedReferenceShapeQueries.ts (10 errors) ==== - // Shape queries (weak types, single signatures, empty object types, string - // index signature only types, function object types) on instantiated interface - // references, including ones whose base types change shape when instantiated. - - type Tagged = { kind: T } & { kind: "a" }; - - declare const other: { other: number }; - - interface Weak extends Tagged { - opt?: number; - } - const weak1: Weak<"b"> = other; - ~~~~~ -!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'Weak<"b">'. - const weak2: Weak<"a"> = other; - ~~~~~ -!!! error TS2741: Property 'kind' is missing in type '{ other: number; }' but required in type 'Weak<"a">'. -!!! related TS2728 instantiatedReferenceShapeQueries.ts:5:20: 'kind' is declared here. - - interface AllOptional { - a?: T; - b?: T[]; - } - interface AllOptionalDerived extends AllOptional { - c?: T; - } - const weak3: AllOptional = other; - ~~~~~ -!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptional'. - const weak4: AllOptionalDerived = other; - ~~~~~ -!!! error TS2559: Type '{ other: number; }' has no properties in common with type 'AllOptionalDerived'. - - interface Empty extends Tagged {} - const empty1: Empty<"b"> = { extra: 1 }; - const empty2: Empty<"a"> = { kind: "a", extra: 1 }; - ~~~~~ -!!! error TS2353: Object literal may only specify known properties, and 'extra' does not exist in type 'Empty<"a">'. - - type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; - interface Overloads extends Two {} - - declare function pipe(f: (...args: A) => B): (...args: A) => B; - - declare const merged: Overloads; - const piped1: number = pipe(merged); - ~~~~~~ -!!! error TS2322: Type '(x: U, y: string) => U' is not assignable to type 'number'. - - declare const distinct: Overloads; - const piped2: number = pipe(distinct); - ~~~~~~ -!!! error TS2322: Type '(x: unknown, y: string) => unknown' is not assignable to type 'number'. - - interface Dict extends Tagged { - [key: string]: unknown; - } - - declare const dict: Dict<"b">; - const dict1 = dict["anything"]; - const dict2: { [key: string]: number } = dict; - ~~~~~ -!!! error TS2322: Type 'Dict<"b">' is not assignable to type '{ [key: string]: number; }'. -!!! error TS2322: 'string' index signatures are incompatible. -!!! error TS2322: Type 'unknown' is not assignable to type 'number'. - - interface Wrap extends T {} - ~~~~ -!!! error TS2430: Interface 'Wrap' incorrectly extends interface 'T'. -!!! error TS2430: 'Wrap' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint '{ a?: string | undefined; }'. - - const wrap1: Wrap<{ a?: string; b: number }> = other; - ~~~~~ -!!! error TS2741: Property 'b' is missing in type '{ other: number; }' but required in type 'Wrap<{ a?: string | undefined; b: number; }>'. -!!! related TS2728 instantiatedReferenceShapeQueries.ts:50:33: 'b' is declared here. - declare const wrap2: Wrap<{ a?: string; (): void }>; - wrap2.bind; - - interface CallableBox { - (): T; - } - interface PlainBox { - value: T; - } - - declare const box: CallableBox | PlainBox; - if (typeof box === "function") { - box; - } - else { - box; - } - \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols deleted file mode 100644 index d82112660bbc1..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.symbols +++ /dev/null @@ -1,215 +0,0 @@ -//// [tests/cases/compiler/instantiatedReferenceShapeQueries.ts] //// - -=== instantiatedReferenceShapeQueries.ts === -// Shape queries (weak types, single signatures, empty object types, string -// index signature only types, function object types) on instantiated interface -// references, including ones whose base types change shape when instantiated. - -type Tagged = { kind: T } & { kind: "a" }; ->Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 4, 12)) ->kind : Symbol(kind, Decl(instantiatedReferenceShapeQueries.ts, 4, 18)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 4, 12)) ->kind : Symbol(kind, Decl(instantiatedReferenceShapeQueries.ts, 4, 32)) - -declare const other: { other: number }; ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 22)) - -interface Weak extends Tagged { ->Weak : Symbol(Weak, Decl(instantiatedReferenceShapeQueries.ts, 6, 39)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 8, 15)) ->Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 8, 15)) - - opt?: number; ->opt : Symbol(Weak.opt, Decl(instantiatedReferenceShapeQueries.ts, 8, 52)) -} -const weak1: Weak<"b"> = other; ->weak1 : Symbol(weak1, Decl(instantiatedReferenceShapeQueries.ts, 11, 5)) ->Weak : Symbol(Weak, Decl(instantiatedReferenceShapeQueries.ts, 6, 39)) ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) - -const weak2: Weak<"a"> = other; ->weak2 : Symbol(weak2, Decl(instantiatedReferenceShapeQueries.ts, 12, 5)) ->Weak : Symbol(Weak, Decl(instantiatedReferenceShapeQueries.ts, 6, 39)) ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) - -interface AllOptional { ->AllOptional : Symbol(AllOptional, Decl(instantiatedReferenceShapeQueries.ts, 12, 31)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 14, 22)) - - a?: T; ->a : Symbol(AllOptional.a, Decl(instantiatedReferenceShapeQueries.ts, 14, 26)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 14, 22)) - - b?: T[]; ->b : Symbol(AllOptional.b, Decl(instantiatedReferenceShapeQueries.ts, 15, 10)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 14, 22)) -} -interface AllOptionalDerived extends AllOptional { ->AllOptionalDerived : Symbol(AllOptionalDerived, Decl(instantiatedReferenceShapeQueries.ts, 17, 1)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 18, 29)) ->AllOptional : Symbol(AllOptional, Decl(instantiatedReferenceShapeQueries.ts, 12, 31)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 18, 29)) - - c?: T; ->c : Symbol(AllOptionalDerived.c, Decl(instantiatedReferenceShapeQueries.ts, 18, 56)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 18, 29)) -} -const weak3: AllOptional = other; ->weak3 : Symbol(weak3, Decl(instantiatedReferenceShapeQueries.ts, 21, 5)) ->AllOptional : Symbol(AllOptional, Decl(instantiatedReferenceShapeQueries.ts, 12, 31)) ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) - -const weak4: AllOptionalDerived = other; ->weak4 : Symbol(weak4, Decl(instantiatedReferenceShapeQueries.ts, 22, 5)) ->AllOptionalDerived : Symbol(AllOptionalDerived, Decl(instantiatedReferenceShapeQueries.ts, 17, 1)) ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) - -interface Empty extends Tagged {} ->Empty : Symbol(Empty, Decl(instantiatedReferenceShapeQueries.ts, 22, 48)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 24, 16)) ->Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 24, 16)) - -const empty1: Empty<"b"> = { extra: 1 }; ->empty1 : Symbol(empty1, Decl(instantiatedReferenceShapeQueries.ts, 25, 5)) ->Empty : Symbol(Empty, Decl(instantiatedReferenceShapeQueries.ts, 22, 48)) ->extra : Symbol(extra, Decl(instantiatedReferenceShapeQueries.ts, 25, 28)) - -const empty2: Empty<"a"> = { kind: "a", extra: 1 }; ->empty2 : Symbol(empty2, Decl(instantiatedReferenceShapeQueries.ts, 26, 5)) ->Empty : Symbol(Empty, Decl(instantiatedReferenceShapeQueries.ts, 22, 48)) ->kind : Symbol(kind, Decl(instantiatedReferenceShapeQueries.ts, 26, 28)) ->extra : Symbol(extra, Decl(instantiatedReferenceShapeQueries.ts, 26, 39)) - -type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; ->Two : Symbol(Two, Decl(instantiatedReferenceShapeQueries.ts, 26, 51)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 28, 9)) ->U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 17)) ->x : Symbol(x, Decl(instantiatedReferenceShapeQueries.ts, 28, 20)) ->U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 17)) ->y : Symbol(y, Decl(instantiatedReferenceShapeQueries.ts, 28, 25)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 28, 9)) ->U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 17)) ->U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 42)) ->x : Symbol(x, Decl(instantiatedReferenceShapeQueries.ts, 28, 45)) ->U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 42)) ->y : Symbol(y, Decl(instantiatedReferenceShapeQueries.ts, 28, 50)) ->U : Symbol(U, Decl(instantiatedReferenceShapeQueries.ts, 28, 42)) - -interface Overloads extends Two {} ->Overloads : Symbol(Overloads, Decl(instantiatedReferenceShapeQueries.ts, 28, 67)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 29, 20)) ->Two : Symbol(Two, Decl(instantiatedReferenceShapeQueries.ts, 26, 51)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 29, 20)) - -declare function pipe(f: (...args: A) => B): (...args: A) => B; ->pipe : Symbol(pipe, Decl(instantiatedReferenceShapeQueries.ts, 29, 40)) ->A : Symbol(A, Decl(instantiatedReferenceShapeQueries.ts, 31, 22)) ->B : Symbol(B, Decl(instantiatedReferenceShapeQueries.ts, 31, 38)) ->f : Symbol(f, Decl(instantiatedReferenceShapeQueries.ts, 31, 42)) ->args : Symbol(args, Decl(instantiatedReferenceShapeQueries.ts, 31, 46)) ->A : Symbol(A, Decl(instantiatedReferenceShapeQueries.ts, 31, 22)) ->B : Symbol(B, Decl(instantiatedReferenceShapeQueries.ts, 31, 38)) ->args : Symbol(args, Decl(instantiatedReferenceShapeQueries.ts, 31, 66)) ->A : Symbol(A, Decl(instantiatedReferenceShapeQueries.ts, 31, 22)) ->B : Symbol(B, Decl(instantiatedReferenceShapeQueries.ts, 31, 38)) - -declare const merged: Overloads; ->merged : Symbol(merged, Decl(instantiatedReferenceShapeQueries.ts, 33, 13)) ->Overloads : Symbol(Overloads, Decl(instantiatedReferenceShapeQueries.ts, 28, 67)) - -const piped1: number = pipe(merged); ->piped1 : Symbol(piped1, Decl(instantiatedReferenceShapeQueries.ts, 34, 5)) ->pipe : Symbol(pipe, Decl(instantiatedReferenceShapeQueries.ts, 29, 40)) ->merged : Symbol(merged, Decl(instantiatedReferenceShapeQueries.ts, 33, 13)) - -declare const distinct: Overloads; ->distinct : Symbol(distinct, Decl(instantiatedReferenceShapeQueries.ts, 36, 13)) ->Overloads : Symbol(Overloads, Decl(instantiatedReferenceShapeQueries.ts, 28, 67)) - -const piped2: number = pipe(distinct); ->piped2 : Symbol(piped2, Decl(instantiatedReferenceShapeQueries.ts, 37, 5)) ->pipe : Symbol(pipe, Decl(instantiatedReferenceShapeQueries.ts, 29, 40)) ->distinct : Symbol(distinct, Decl(instantiatedReferenceShapeQueries.ts, 36, 13)) - -interface Dict extends Tagged { ->Dict : Symbol(Dict, Decl(instantiatedReferenceShapeQueries.ts, 37, 38)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 39, 15)) ->Tagged : Symbol(Tagged, Decl(instantiatedReferenceShapeQueries.ts, 0, 0)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 39, 15)) - - [key: string]: unknown; ->key : Symbol(key, Decl(instantiatedReferenceShapeQueries.ts, 40, 5)) -} - -declare const dict: Dict<"b">; ->dict : Symbol(dict, Decl(instantiatedReferenceShapeQueries.ts, 43, 13)) ->Dict : Symbol(Dict, Decl(instantiatedReferenceShapeQueries.ts, 37, 38)) - -const dict1 = dict["anything"]; ->dict1 : Symbol(dict1, Decl(instantiatedReferenceShapeQueries.ts, 44, 5)) ->dict : Symbol(dict, Decl(instantiatedReferenceShapeQueries.ts, 43, 13)) - -const dict2: { [key: string]: number } = dict; ->dict2 : Symbol(dict2, Decl(instantiatedReferenceShapeQueries.ts, 45, 5)) ->key : Symbol(key, Decl(instantiatedReferenceShapeQueries.ts, 45, 16)) ->dict : Symbol(dict, Decl(instantiatedReferenceShapeQueries.ts, 43, 13)) - -interface Wrap extends T {} ->Wrap : Symbol(Wrap, Decl(instantiatedReferenceShapeQueries.ts, 45, 46)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 47, 15)) ->a : Symbol(a, Decl(instantiatedReferenceShapeQueries.ts, 47, 26)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 47, 15)) - -const wrap1: Wrap<{ a?: string; b: number }> = other; ->wrap1 : Symbol(wrap1, Decl(instantiatedReferenceShapeQueries.ts, 49, 5)) ->Wrap : Symbol(Wrap, Decl(instantiatedReferenceShapeQueries.ts, 45, 46)) ->a : Symbol(a, Decl(instantiatedReferenceShapeQueries.ts, 49, 19)) ->b : Symbol(b, Decl(instantiatedReferenceShapeQueries.ts, 49, 31)) ->other : Symbol(other, Decl(instantiatedReferenceShapeQueries.ts, 6, 13)) - -declare const wrap2: Wrap<{ a?: string; (): void }>; ->wrap2 : Symbol(wrap2, Decl(instantiatedReferenceShapeQueries.ts, 50, 13)) ->Wrap : Symbol(Wrap, Decl(instantiatedReferenceShapeQueries.ts, 45, 46)) ->a : Symbol(a, Decl(instantiatedReferenceShapeQueries.ts, 50, 27)) - -wrap2.bind; ->wrap2.bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) ->wrap2 : Symbol(wrap2, Decl(instantiatedReferenceShapeQueries.ts, 50, 13)) ->bind : Symbol(CallableFunction.bind, Decl(lib.es5.d.ts, --, --), Decl(lib.es5.d.ts, --, --)) - -interface CallableBox { ->CallableBox : Symbol(CallableBox, Decl(instantiatedReferenceShapeQueries.ts, 51, 11)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 53, 22)) - - (): T; ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 53, 22)) -} -interface PlainBox { ->PlainBox : Symbol(PlainBox, Decl(instantiatedReferenceShapeQueries.ts, 55, 1)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 56, 19)) - - value: T; ->value : Symbol(PlainBox.value, Decl(instantiatedReferenceShapeQueries.ts, 56, 23)) ->T : Symbol(T, Decl(instantiatedReferenceShapeQueries.ts, 56, 19)) -} - -declare const box: CallableBox | PlainBox; ->box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) ->CallableBox : Symbol(CallableBox, Decl(instantiatedReferenceShapeQueries.ts, 51, 11)) ->PlainBox : Symbol(PlainBox, Decl(instantiatedReferenceShapeQueries.ts, 55, 1)) - -if (typeof box === "function") { ->box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) - - box; ->box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) -} -else { - box; ->box : Symbol(box, Decl(instantiatedReferenceShapeQueries.ts, 60, 13)) -} - diff --git a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types b/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types deleted file mode 100644 index cc797f4c91075..0000000000000 --- a/tsc/testdata/baselines/reference/compiler/instantiatedReferenceShapeQueries.types +++ /dev/null @@ -1,157 +0,0 @@ -//// [tests/cases/compiler/instantiatedReferenceShapeQueries.ts] //// - -=== instantiatedReferenceShapeQueries.ts === -// Shape queries (weak types, single signatures, empty object types, string -// index signature only types, function object types) on instantiated interface -// references, including ones whose base types change shape when instantiated. - -type Tagged = { kind: T } & { kind: "a" }; ->Tagged : Tagged ->kind : T ->kind : "a" - -declare const other: { other: number }; ->other : { other: number; } ->other : number - -interface Weak extends Tagged { - opt?: number; ->opt : number | undefined -} -const weak1: Weak<"b"> = other; ->weak1 : Weak<"b"> ->other : { other: number; } - -const weak2: Weak<"a"> = other; ->weak2 : Weak<"a"> ->other : { other: number; } - -interface AllOptional { - a?: T; ->a : T | undefined - - b?: T[]; ->b : T[] | undefined -} -interface AllOptionalDerived extends AllOptional { - c?: T; ->c : T | undefined -} -const weak3: AllOptional = other; ->weak3 : AllOptional ->other : { other: number; } - -const weak4: AllOptionalDerived = other; ->weak4 : AllOptionalDerived ->other : { other: number; } - -interface Empty extends Tagged {} -const empty1: Empty<"b"> = { extra: 1 }; ->empty1 : Empty<"b"> ->{ extra: 1 } : { extra: number; } ->extra : number ->1 : 1 - -const empty2: Empty<"a"> = { kind: "a", extra: 1 }; ->empty2 : Empty<"a"> ->{ kind: "a", extra: 1 } : { kind: "a"; extra: number; } ->kind : "a" ->"a" : "a" ->extra : number ->1 : 1 - -type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; ->Two : Two ->x : U ->y : T ->x : U ->y : string - -interface Overloads extends Two {} - -declare function pipe(f: (...args: A) => B): (...args: A) => B; ->pipe : (f: (...args: A) => B) => (...args: A) => B ->f : (...args: A) => B ->args : A ->args : A - -declare const merged: Overloads; ->merged : Overloads - -const piped1: number = pipe(merged); ->piped1 : number ->pipe(merged) : (x: U, y: string) => U ->pipe : (f: (...args: A) => B) => (...args: A) => B ->merged : Overloads - -declare const distinct: Overloads; ->distinct : Overloads - -const piped2: number = pipe(distinct); ->piped2 : number ->pipe(distinct) : (x: unknown, y: string) => unknown ->pipe : (f: (...args: A) => B) => (...args: A) => B ->distinct : Overloads - -interface Dict extends Tagged { - [key: string]: unknown; ->key : string -} - -declare const dict: Dict<"b">; ->dict : Dict<"b"> - -const dict1 = dict["anything"]; ->dict1 : unknown ->dict["anything"] : unknown ->dict : Dict<"b"> ->"anything" : "anything" - -const dict2: { [key: string]: number } = dict; ->dict2 : { [key: string]: number; } ->key : string ->dict : Dict<"b"> - -interface Wrap extends T {} ->a : string | undefined - -const wrap1: Wrap<{ a?: string; b: number }> = other; ->wrap1 : Wrap<{ a?: string; b: number; }> ->a : string | undefined ->b : number ->other : { other: number; } - -declare const wrap2: Wrap<{ a?: string; (): void }>; ->wrap2 : Wrap<{ (): void; a?: string; }> ->a : string | undefined - -wrap2.bind; ->wrap2.bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } ->wrap2 : Wrap<{ (): void; a?: string; }> ->bind : { (this: T, thisArg: ThisParameterType): OmitThisParameter; (this: (this: T, ...args: [...A, ...B]) => R, thisArg: T, ...args: A): (...args: B) => R; } - -interface CallableBox { - (): T; -} -interface PlainBox { - value: T; ->value : T -} - -declare const box: CallableBox | PlainBox; ->box : CallableBox | PlainBox - -if (typeof box === "function") { ->typeof box === "function" : boolean ->typeof box : "bigint" | "boolean" | "function" | "number" | "object" | "string" | "symbol" | "undefined" ->box : CallableBox | PlainBox ->"function" : "function" - - box; ->box : CallableBox -} -else { - box; ->box : PlainBox -} - diff --git a/tsc/testdata/tests/cases/compiler/instantiatedReferenceLazyMembers.ts b/tsc/testdata/tests/cases/compiler/instantiatedReferenceLazyMembers.ts new file mode 100644 index 0000000000000..c6a92015257eb --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/instantiatedReferenceLazyMembers.ts @@ -0,0 +1,74 @@ +// @strict: true +// @target: esnext +// @noEmit: true + +// Member lookups and shape queries on instantiated class and interface +// references, including base types whose shape depends on the instantiation. + +interface Base { + value: T; + shared: string; +} +interface Derived extends Base { + shared: "derived"; +} +declare const derived: Derived; +const derived1 = derived.value; +const derived2 = derived.shared; +derived.missing; + +class Box { + private secret!: T; + contents!: T; +} +declare const box: Box; +const box1 = box.contents; +box.secret; + +// Tagged<"b"> reduces to never, Tagged<"a"> doesn't. +type Tagged = { kind: T } & { kind: "a" }; +declare const other: { other: number }; + +interface Weak extends Tagged { + opt?: number; +} +const weak1: Weak<"b"> = other; +const weak2: Weak<"a"> = other; + +type Callable = { (): void; kind: T } & { kind: "a" }; +interface Fn extends Callable {} +declare const neverFn: Fn<"b">; +neverFn.bind; +declare const fn: Fn<"a">; +const fn1 = fn.bind; + +interface Dict extends Tagged { + [key: string]: unknown; +} +declare const dict: Dict<"b">; +const dict1: { [key: string]: number } = dict; + +interface Wrap extends T {} +const wrap1: Wrap<{ a?: string; b: number }> = other; + +type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; +interface Overloads extends Two {} +declare function pipe(f: (...args: A) => B): (...args: A) => B; +declare const merged: Overloads; +const piped1: number = pipe(merged); +declare const distinct: Overloads; +const piped2: number = pipe(distinct); + +interface Tree extends Array> { + value: T; +} +declare const tree: Tree; +const tree1 = tree.map(child => child.value); + +interface CallableBox { + (): T; +} +declare const boxOrCallable: CallableBox | Box; +if (typeof boxOrCallable === "function") { + boxOrCallable; +} diff --git a/tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts b/tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts deleted file mode 100644 index 8d04bdcf9e124..0000000000000 --- a/tsc/testdata/tests/cases/compiler/instantiatedReferenceMemberLookup.ts +++ /dev/null @@ -1,85 +0,0 @@ -// @strict: true -// @target: esnext -// @noEmit: true - -// Property lookups on instantiated class and interface references: declared -// and inherited members, members hidden by redeclarations, accessibility, -// `this` types, Object and Function members, and base types that change shape -// when instantiated. - -interface Base { - value: T; - method(): T; - shared: string; -} - -interface Derived extends Base { - shared: "derived"; - own: T; -} - -declare const d: Derived; -const d1 = d.value; -const d2 = d.method(); -const d3 = d.shared; -const d4 = d.own; -const d5 = d.toString(); -d.missing; - -class Box { - private secret!: T; - protected guarded!: T; - contents!: T; - self(): this { - return this; - } -} - -class NumberBox extends Box { - read() { - return this.guarded; - } -} - -declare const box: Box; -const box1 = box.contents; -const box2 = box.self().contents; -box.secret; -box.guarded; - -declare const numberBox: NumberBox; -const numberBox1 = numberBox.self().read(); - -type Callable = { (): void; kind: T } & { kind: "a" }; -interface Fn extends Callable {} - -declare const neverFn: Fn<"b">; -neverFn.bind; -neverFn.kind; - -declare const fn: Fn<"a">; -const fn1 = fn.bind; -const fn2 = fn.kind; - -interface Newable { - new (): T; -} -interface NewableBox extends Newable> {} - -declare const newable: NewableBox; -const newable1 = newable.prototype; -const newable2 = new newable().contents; - -interface Wrap extends T {} - -declare const wrap: Wrap<{ a: string; b: number }>; -const wrap1 = wrap.b; -wrap.c; - -interface Tree extends Array> { - value: T; -} - -declare const tree: Tree; -const tree1 = tree[0].value; -const tree2 = tree.map(child => child.value); diff --git a/tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts b/tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts deleted file mode 100644 index 20c1b8ad4ed42..0000000000000 --- a/tsc/testdata/tests/cases/compiler/instantiatedReferenceShapeQueries.ts +++ /dev/null @@ -1,71 +0,0 @@ -// @strict: true -// @target: esnext -// @noEmit: true - -// Shape queries (weak types, single signatures, empty object types, string -// index signature only types, function object types) on instantiated interface -// references, including ones whose base types change shape when instantiated. - -type Tagged = { kind: T } & { kind: "a" }; - -declare const other: { other: number }; - -interface Weak extends Tagged { - opt?: number; -} -const weak1: Weak<"b"> = other; -const weak2: Weak<"a"> = other; - -interface AllOptional { - a?: T; - b?: T[]; -} -interface AllOptionalDerived extends AllOptional { - c?: T; -} -const weak3: AllOptional = other; -const weak4: AllOptionalDerived = other; - -interface Empty extends Tagged {} -const empty1: Empty<"b"> = { extra: 1 }; -const empty2: Empty<"a"> = { kind: "a", extra: 1 }; - -type Two = { (x: U, y: T): U } & { (x: U, y: string): U }; -interface Overloads extends Two {} - -declare function pipe(f: (...args: A) => B): (...args: A) => B; - -declare const merged: Overloads; -const piped1: number = pipe(merged); - -declare const distinct: Overloads; -const piped2: number = pipe(distinct); - -interface Dict extends Tagged { - [key: string]: unknown; -} - -declare const dict: Dict<"b">; -const dict1 = dict["anything"]; -const dict2: { [key: string]: number } = dict; - -interface Wrap extends T {} - -const wrap1: Wrap<{ a?: string; b: number }> = other; -declare const wrap2: Wrap<{ a?: string; (): void }>; -wrap2.bind; - -interface CallableBox { - (): T; -} -interface PlainBox { - value: T; -} - -declare const box: CallableBox | PlainBox; -if (typeof box === "function") { - box; -} -else { - box; -} From 2aefafb51b54044b51d2ea66ebd6dad1bc5df28e Mon Sep 17 00:00:00 2001 From: Max schwenk Date: Tue, 29 Sep 2026 03:34:24 -0400 Subject: [PATCH 9/9] Trim comments Co-Authored-By: Claude Opus 5.5 --- tsc/internal/checker/checker.go | 65 +++++++-------------------------- 1 file changed, 14 insertions(+), 51 deletions(-) diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 92b96c2d314eb..838c7ddc2847f 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -19500,8 +19500,6 @@ func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { return nil } -// appendInheritedSignaturesAndIndexInfos appends the signatures and index infos -// a class or interface inherits from baseType. func (c *Checker) appendInheritedSignaturesAndIndexInfos(callSignatures []*Signature, constructSignatures []*Signature, indexInfos []*IndexInfo, baseType *Type) ([]*Signature, []*Signature, []*IndexInfo) { callSignatures = core.Concatenate(callSignatures, c.getSignaturesOfType(baseType, SignatureKindCall)) constructSignatures = core.Concatenate(constructSignatures, c.getSignaturesOfType(baseType, SignatureKindConstruct)) @@ -19517,36 +19515,22 @@ func (c *Checker) appendInheritedSignaturesAndIndexInfos(callSignatures []*Signa return callSignatures, constructSignatures, indexInfos } -// Resolving the members of an instantiated class or interface reference -// instantiates every declared member and merges every inherited one, even when -// the checker only needs one of them (`expect(x).toBe`, `arr.map`) or asks -// about the shape of the type (isWeakType, getSingleSignature). In large -// programs most of those symbols are never used. -// -// Such a reference instead gets a lazy member table on first use. Preparing -// the table does what resolveObjectTypeMembers does, in the same order, except -// creating member symbols: signatures and index infos are instantiated and -// inherited as usual, and getSignaturesOfStructuredType and -// getIndexInfosOfStructuredType answer from the table. Member lookups -// instantiate only the requested member, the way resolveObjectTypeMembers -// would have when the table was prepared, and resolving the members in full -// later reuses the symbols already handed out. While a table is being -// prepared, the type resolves its members as usual. +// Instantiated class and interface references get a lazy member table in place of +// resolved members. It has the signatures and index infos, but only instantiates the +// members that are looked up, and reuses them if the members are later resolved in full. type lazyMemberTable struct { ready bool mapper *TypeMapper typeArguments []*Type - unaffected []string // sorted names of declared members that instantiate to themselves - callSignatures []*Signature // declared and inherited + unaffected []string // sorted names of declared members that instantiate to themselves + callSignatures []*Signature constructSignatures []*Signature indexInfos []*IndexInfo - baseTypes []*Type // instantiated base types + baseTypes []*Type declared map[string]*ast.Symbol } -// mayHaveLazyMembers is a quick check for hot paths, as most types they see -// have resolved members. func mayHaveLazyMembers(t *Type) bool { return t.objectFlags&(ObjectFlagsMembersResolved|ObjectFlagsReference) == ObjectFlagsReference } @@ -19560,9 +19544,7 @@ func (c *Checker) getReferenceMemberTypeArguments(t *Type, source *Type) (typePa return typeParameters, typeArguments } -// getReadyLazyMemberTable returns the lazy member table of t, preparing it -// first if needed, or nil if t's members aren't resolved lazily or the table -// is still being prepared. +// Returns nil if t has no lazy member table or it is still being prepared. func (c *Checker) getReadyLazyMemberTable(t *Type) *lazyMemberTable { if !mayHaveLazyMembers(t) { return nil @@ -19571,8 +19553,6 @@ func (c *Checker) getReadyLazyMemberTable(t *Type) *lazyMemberTable { } func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { - // Only references to generic classes and interfaces, other than the - // declaration itself, have lazy member tables. source := t.Target() if t.flags&TypeFlagsObject == 0 || source == nil || source == t || source.objectFlags&ObjectFlagsClassOrInterface == 0 || source.objectFlags&ObjectFlagsTuple != 0 || t.symbol != nil && t.symbol.Flags&ast.SymbolFlagsValueModule != 0 { @@ -19598,13 +19578,11 @@ func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { return lm } -// prepareLazyMembers mirrors resolveObjectTypeMembers, except that it records -// which declared members would instantiate to themselves instead of -// instantiating them, and doesn't inherit members from base types with lazy -// member tables. +// Mirrors resolveObjectTypeMembers without creating member symbols. func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { source := t.Target() resolved := c.resolveDeclaredMembers(source) + // Whether instantiateSymbol returns a member itself depends on what is resolved now. for id, symbol := range resolved.declaredMembers { if c.isNamedMember(symbol, id) && c.isSymbolUnaffectedByInstantiation(symbol, lm.mapper) { lm.unaffected = append(lm.unaffected, id) @@ -19629,14 +19607,11 @@ func (c *Checker) prepareLazyMembers(t *Type, lm *lazyMemberTable) { lm.callSignatures, lm.constructSignatures, lm.indexInfos = callSignatures, constructSignatures, indexInfos lm.ready = true if t.objectFlags&ObjectFlagsMembersResolved != 0 { - // Preparing the table led to t being resolved in full, and - // resolveObjectTypeMembers would now replace those members. + // t was resolved while preparing; resolveObjectTypeMembers would now replace its members. c.resolveLazyMembers(t, lm) } } -// resolveLazyMembers resolves the members of t from its lazy member table, as -// resolveObjectTypeMembers does. func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { resolved := c.resolveDeclaredMembers(t.Target()) var members ast.SymbolTable @@ -19655,8 +19630,6 @@ func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { delete(c.lazyMemberTables, t) } -// getLazyDeclaredMember instantiates the declared member symbol named name as -// resolveObjectTypeMembers would have when the table was prepared. func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { result := lm.declared[name] if result == nil { @@ -19669,8 +19642,6 @@ func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, return result } -// getMemberOfStructuredType returns c.resolveStructuredTypeMembers(t).members[name] -// without resolving the members of t if they are resolved lazily. func (c *Checker) getMemberOfStructuredType(t *Type, name string) *ast.Symbol { if t.objectFlags&ObjectFlagsMembersResolved != 0 { return t.AsStructuredType().members[name] @@ -19683,9 +19654,7 @@ func (c *Checker) getMemberOfUnresolvedStructuredType(t *Type, name string) *ast if lm == nil || isReservedMemberName(name) { return c.resolveStructuredTypeMembers(t).members[name] } - // As in resolveObjectTypeMembers: the declared member, or else the property - // inherited from the first base type that has one, where a base may only - // fill a missing or non-value entry (addInheritedMembers). + // The declared member, else the first base type's property (see addInheritedMembers). var result *ast.Symbol if decl := c.resolveDeclaredMembers(t.Target()).declaredMembers[name]; decl != nil && c.isNamedMember(decl, name) { result = c.getLazyDeclaredMember(lm, decl, name) @@ -19701,9 +19670,7 @@ func (c *Checker) getMemberOfUnresolvedStructuredType(t *Type, name string) *ast return result } -// everyPropertyOfStructuredType is core.Every(c.resolveStructuredTypeMembers(t).properties, f), -// without resolving the members of t if they are resolved lazily. f may see a -// declared member instead of its instantiation, which has the same flags. +// f may see a declared member instead of its instantiation, which has the same flags. func (c *Checker) everyPropertyOfStructuredType(t *Type, f func(prop *ast.Symbol) bool) bool { if lm := c.getReadyLazyMemberTable(t); lm != nil { var seen collections.Set[string] @@ -19716,9 +19683,7 @@ func (c *Checker) hasPropertiesOfStructuredType(t *Type) bool { return !c.everyPropertyOfStructuredType(t, func(*ast.Symbol) bool { return false }) } -// everyLazyProperty reports whether f holds for every property of t. As in -// addInheritedMembers, a property hides inherited properties of the same -// name; seen tracks those names. +// seen has the names of properties that hide inherited ones, as in addInheritedMembers. func (c *Checker) everyLazyProperty(t *Type, lm *lazyMemberTable, seen *collections.Set[string], f func(prop *ast.Symbol) bool) bool { for id, symbol := range c.resolveDeclaredMembers(t.Target()).declaredMembers { if c.isNamedMember(symbol, id) && seen.AddIfAbsent(id) && !f(symbol) { @@ -21336,9 +21301,7 @@ func (c *Checker) instantiateSymbol(symbol *ast.Symbol, m *TypeMapper) *ast.Symb return c.newInstantiatedSymbol(symbol, m) } -// isSymbolUnaffectedByInstantiation reports whether instantiateSymbol returns -// the symbol itself. That depends on whether the type of the symbol has been -// resolved, so the answer can change from false to true. +// Can change from false to true once the type of the symbol is resolved. func (c *Checker) isSymbolUnaffectedByInstantiation(symbol *ast.Symbol, m *TypeMapper) bool { links := c.valueSymbolLinks.Get(symbol) if m != nil && m.MapsThisOnly() && isThisless(symbol) {