diff --git a/tsc/internal/checker/checker.go b/tsc/internal/checker/checker.go index 13cb56c69ded6..10593676b518e 100644 --- a/tsc/internal/checker/checker.go +++ b/tsc/internal/checker/checker.go @@ -700,6 +700,8 @@ type Checker struct { sourceFileLinks core.LinkStore[*ast.SourceFile, SourceFileLinks] regExpScanner *scanner.Scanner patternForType map[*Type]*ast.Node + lazyMemberTables map[*Type]*lazyMemberTable + lazyMappedTables map[*Type]*lazyMappedTable contextFreeTypes map[*ast.Node]*Type anyType *Type autoType *Type @@ -980,6 +982,8 @@ 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.lazyMappedTables = make(map[*Type]*lazyMappedTable) c.contextFreeTypes = make(map[*ast.Node]*Type) c.anyType = c.newIntrinsicType(TypeFlagsAny, "any") c.autoType = c.newIntrinsicTypeEx(TypeFlagsAny, "any", ObjectFlagsNonInferrableType) @@ -19240,8 +19244,7 @@ func (c *Checker) getPropertyOfTypeEx(t *Type, name string, skipObjectFunctionPr t = c.getReducedApparentType(t) switch { case t.flags&TypeFlagsObject != 0: - 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 @@ -19260,9 +19263,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 { @@ -19304,6 +19307,16 @@ func (c *Checker) getSignaturesOfStructuredType(t *Type, kind SignatureKind) []* if t.flags&TypeFlagsStructuredType == 0 { return nil } + if lm := c.getReadyLazyMemberTable(t); lm != nil { + if kind == SignatureKindCall { + return lm.callSignatures + } + return lm.constructSignatures + } + if t.objectFlags&ObjectFlagsMapped != 0 { + // Mapped types have no signatures. + return nil + } resolved := c.resolveStructuredTypeMembers(t) if kind == SignatureKindCall { return resolved.signatures[:resolved.callSignatureCount] @@ -19317,6 +19330,12 @@ func (c *Checker) getIndexInfosOfType(t *Type) []*IndexInfo { func (c *Checker) getIndexInfosOfStructuredType(t *Type) []*IndexInfo { if t.flags&TypeFlagsStructuredType != 0 { + if lm := c.getReadyLazyMemberTable(t); lm != nil { + return lm.indexInfos + } + if lazy := c.getLazyMappedTable(t); lazy != nil { + return c.getLazyMappedTypeIndexInfos(t, lazy) + } return c.resolveStructuredTypeMembers(t).indexInfos } return nil @@ -19433,13 +19452,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.ready { + c.resolveLazyMembers(t, lm) + return } + source := t.Target() + typeParameters, paddedTypeArguments := c.getReferenceMemberTypeArguments(t, source) c.resolveObjectTypeMembers(t, source, typeParameters, paddedTypeArguments) } @@ -19476,17 +19494,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) } } c.setStructuredTypeMembers(t, members, callSignatures, constructSignatures, indexInfos) @@ -19501,6 +19509,220 @@ func findIndexInfo(indexInfos []*IndexInfo, keyType *Type) *IndexInfo { return nil } +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 +} + +// 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 + constructSignatures []*Signature + indexInfos []*IndexInfo + baseTypes []*Type + declared map[string]*ast.Symbol +} + +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 +} + +// 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 + } + return c.getReadyLazyMemberTableWorker(t) +} + +func (c *Checker) getReadyLazyMemberTableWorker(t *Type) *lazyMemberTable { + 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] + 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{}, + } + c.lazyMemberTables[t] = lm + c.prepareLazyMembers(t, lm) + } + if !lm.ready || t.objectFlags&ObjectFlagsMembersResolved != 0 { + return nil + } + return lm +} + +// 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) + } + } + slices.Sort(lm.unaffected) + 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 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 reduced := c.getReducedApparentType(instantiatedBaseType); reduced.flags&TypeFlagsIntersection == 0 && c.getReadyLazyMemberTable(reduced) == nil { + c.getPropertiesOfType(instantiatedBaseType) + } + callSignatures, constructSignatures, indexInfos = c.appendInheritedSignaturesAndIndexInfos(callSignatures, constructSignatures, indexInfos, instantiatedBaseType) + } + lm.callSignatures, lm.constructSignatures, lm.indexInfos = callSignatures, constructSignatures, indexInfos + lm.ready = true + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + // t was resolved while preparing; resolveObjectTypeMembers would now replace its members. + c.resolveLazyMembers(t, lm) + } +} + +func (c *Checker) resolveLazyMembers(t *Type, lm *lazyMemberTable) { + resolved := c.resolveDeclaredMembers(t.Target()) + 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) + } + } + } + for _, baseType := range lm.baseTypes { + members = c.addInheritedMembers(members, c.getPropertiesOfType(baseType)) + } + c.setStructuredTypeMembers(t, members, lm.callSignatures, lm.constructSignatures, lm.indexInfos) + delete(c.lazyMemberTables, t) +} + +func (c *Checker) getLazyDeclaredMember(lm *lazyMemberTable, symbol *ast.Symbol, name string) *ast.Symbol { + result := lm.declared[name] + if result == nil { + result = symbol + if _, unaffected := slices.BinarySearch(lm.unaffected, name); !unaffected { + result = c.newInstantiatedSymbol(symbol, lm.mapper) + } + lm.declared[name] = result + } + return result +} + +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 t.objectFlags&ObjectFlagsMapped != 0 && !isReservedMemberName(name) { + if lazy := c.getLazyMappedTable(t); lazy != nil { + if member, ok := c.getLazyMappedTypeMember(t, lazy, name); ok { + return member + } + } + } + lm := c.getReadyLazyMemberTable(t) + if lm == nil || isReservedMemberName(name) { + return c.resolveStructuredTypeMembers(t).members[name] + } + // 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) + } + for _, baseType := range lm.baseTypes { + if result != nil && result.Flags&ast.SymbolFlagsValue != 0 { + break + } + if prop := c.getPropertyOfTypeEx(baseType, name, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/); prop != nil && !isStaticPrivateIdentifierProperty(prop) { + result = prop + } + } + return result +} + +// 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 }) +} + +// 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) { + 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 !isStaticPrivateIdentifierProperty(prop) && seen.AddIfAbsent(prop.Name) && !f(prop) { + return false + } + } + } + return true +} + func (c *Checker) getBaseTypes(t *Type) []*Type { if t.objectFlags&(ObjectFlagsClassOrInterface|ObjectFlagsTuple) == 0 { return nil @@ -19689,13 +19911,14 @@ func (c *Checker) getSingleCallOrConstructSignature(t *Type) *Signature { func (c *Checker) getSingleSignature(t *Type, kind SignatureKind, allowMembers bool) *Signature { if t.flags&TypeFlagsObject != 0 { - 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 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] } - 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] } } } @@ -21088,24 +21311,34 @@ 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) +} + +// 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) { - 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 @@ -21245,6 +21478,8 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { // The 'T' in 'keyof T' templateModifiers := getMappedTypeModifiers(t) include := TypeFlagsStringOrNumberLiteralOrUnique + lazy := c.lazyMappedTables[t] + delete(c.lazyMappedTables, t) addMemberForKeyTypeWorker := func(keyType *Type, propNameType *Type) { // If the current iteration type constituent is a string literal type, create a property. // Otherwise, for type string create a string index signature. @@ -21258,46 +21493,13 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { valueLinks.nameType = c.getUnionType([]*Type{valueLinks.nameType, propNameType}) mappedLinks := c.mappedSymbolLinks.Get(existingProp) mappedLinks.keyType = c.getUnionType([]*Type{mappedLinks.keyType, keyType}) + } else if lazy != nil && lazy.members[propName] != nil { + members[propName] = lazy.members[propName] } else { - var modifiersProp *ast.Symbol - if isTypeUsableAsPropertyName(keyType) { - modifiersProp = c.getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) - } - isOptional := templateModifiers&MappedTypeModifiersIncludeOptional != 0 || templateModifiers&MappedTypeModifiersExcludeOptional == 0 && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0 - isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersProp != nil && c.isReadonlySymbol(modifiersProp) - stripOptional := c.strictNullChecks && !isOptional && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0 - var lateFlag ast.CheckFlags - if modifiersProp != nil { - lateFlag = modifiersProp.CheckFlags & ast.CheckFlagsLate - } - prop := c.newSymbol(ast.SymbolFlagsProperty|core.IfElse(isOptional, ast.SymbolFlagsOptional, 0), propName) - prop.CheckFlags = lateFlag | ast.CheckFlagsMapped | core.IfElse(isReadonly, ast.CheckFlagsReadonly, 0) | core.IfElse(stripOptional, ast.CheckFlagsStripOptional, 0) - valueLinks := c.valueSymbolLinks.Get(prop) - valueLinks.containingType = t - valueLinks.nameType = propNameType - mappedLinks := c.mappedSymbolLinks.Get(prop) - mappedLinks.keyType = keyType - if modifiersProp != nil { - mappedLinks.syntheticOrigin = modifiersProp - if shouldLinkPropDeclarations { - prop.Declarations = modifiersProp.Declarations - } - } - members[propName] = prop - } - } else if c.isValidIndexKeyType(propNameType) || propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) != 0 { - indexKeyType := propNameType - switch { - case propNameType.flags&(TypeFlagsAny|TypeFlagsString) != 0: - indexKeyType = c.stringType - case propNameType.flags&(TypeFlagsNumber|TypeFlagsEnum) != 0: - indexKeyType = c.numberType + members[propName] = c.newMappedTypeMember(t, modifiersType, templateModifiers, shouldLinkPropDeclarations, keyType, propNameType, propName) } - propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) - modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType) - isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly - indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil) - indexInfos = c.appendIndexInfo(indexInfos, indexInfo, true /*union*/) + } else if lazy == nil || !lazy.indexInfosReady { + indexInfos = c.appendMappedTypeIndexInfo(indexInfos, t, modifiersType, typeParameter, templateType, templateModifiers, keyType, propNameType) } } addMemberForKeyType := func(keyType *Type) { @@ -21315,9 +21517,149 @@ func (c *Checker) resolveMappedTypeMembers(t *Type) { } else { forEachType(c.getLowerBoundOfKeyType(constraintType), addMemberForKeyType) } + if lazy != nil && lazy.indexInfosReady { + indexInfos = lazy.indexInfos + } c.setStructuredTypeMembers(t, members, nil, nil, indexInfos) } +func (c *Checker) appendMappedTypeIndexInfo(indexInfos []*IndexInfo, t *Type, modifiersType *Type, typeParameter *Type, templateType *Type, templateModifiers MappedTypeModifiers, keyType *Type, propNameType *Type) []*IndexInfo { + if !c.isValidIndexKeyType(propNameType) && propNameType.flags&(TypeFlagsAny|TypeFlagsEnum) == 0 { + return indexInfos + } + indexKeyType := propNameType + switch { + case propNameType.flags&(TypeFlagsAny|TypeFlagsString) != 0: + indexKeyType = c.stringType + case propNameType.flags&(TypeFlagsNumber|TypeFlagsEnum) != 0: + indexKeyType = c.numberType + } + propType := c.instantiateType(templateType, appendTypeMapping(t.AsMappedType().mapper, typeParameter, keyType)) + modifiersIndexInfo := c.getApplicableIndexInfo(modifiersType, propNameType) + isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersIndexInfo != nil && modifiersIndexInfo.isReadonly + indexInfo := c.newIndexInfo(indexKeyType, propType, isReadonly, nil, nil) + return c.appendIndexInfo(indexInfos, indexInfo, true /*union*/) +} + +func (c *Checker) newMappedTypeMember(t *Type, modifiersType *Type, templateModifiers MappedTypeModifiers, shouldLinkPropDeclarations bool, keyType *Type, propNameType *Type, propName string) *ast.Symbol { + var modifiersProp *ast.Symbol + if isTypeUsableAsPropertyName(keyType) { + modifiersProp = c.getPropertyOfType(modifiersType, getPropertyNameFromType(keyType)) + } + isOptional := templateModifiers&MappedTypeModifiersIncludeOptional != 0 || templateModifiers&MappedTypeModifiersExcludeOptional == 0 && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0 + isReadonly := templateModifiers&MappedTypeModifiersIncludeReadonly != 0 || templateModifiers&MappedTypeModifiersExcludeReadonly == 0 && modifiersProp != nil && c.isReadonlySymbol(modifiersProp) + stripOptional := c.strictNullChecks && !isOptional && modifiersProp != nil && modifiersProp.Flags&ast.SymbolFlagsOptional != 0 + var lateFlag ast.CheckFlags + if modifiersProp != nil { + lateFlag = modifiersProp.CheckFlags & ast.CheckFlagsLate + } + prop := c.newSymbol(ast.SymbolFlagsProperty|core.IfElse(isOptional, ast.SymbolFlagsOptional, 0), propName) + prop.CheckFlags = lateFlag | ast.CheckFlagsMapped | core.IfElse(isReadonly, ast.CheckFlagsReadonly, 0) | core.IfElse(stripOptional, ast.CheckFlagsStripOptional, 0) + valueLinks := c.valueSymbolLinks.Get(prop) + valueLinks.containingType = t + valueLinks.nameType = propNameType + mappedLinks := c.mappedSymbolLinks.Get(prop) + mappedLinks.keyType = keyType + if modifiersProp != nil { + mappedLinks.syntheticOrigin = modifiersProp + if shouldLinkPropDeclarations { + prop.Declarations = modifiersProp.Declarations + } + } + return prop +} + +// Mapped types { [P in keyof T]: X } where T is an object type get a lazy table that creates +// members and index infos as they are asked for, the way resolveMappedTypeMembers would. +type lazyMappedTable struct { + typeParameter *Type + templateType *Type + modifiersType *Type + templateModifiers MappedTypeModifiers + shouldLinkPropDeclarations bool + members ast.SymbolTable + indexInfos []*IndexInfo + indexInfosReady bool + resolving bool +} + +func (c *Checker) getLazyMappedTable(t *Type) *lazyMappedTable { + if t.objectFlags&(ObjectFlagsMapped|ObjectFlagsMembersResolved) != ObjectFlagsMapped { + return nil + } + if lazy := c.lazyMappedTables[t]; lazy != nil { + return lazy + } + if !c.isMappedTypeWithKeyofConstraintDeclaration(t) { + return nil + } + // The same steps as resolveMappedTypeMembers before it creates members. + typeParameter := c.getTypeParameterFromMappedType(t) + c.getConstraintTypeFromMappedType(t) + mappedType := core.OrElse(t.AsMappedType().target, t) + if c.getNameTypeFromMappedType(mappedType) != nil { + return nil + } + shouldLinkPropDeclarations := c.getMappedTypeNameTypeKind(mappedType) != MappedTypeNameTypeKindRemapping + templateType := c.getTemplateTypeFromMappedType(mappedType) + modifiersType := c.getApparentType(c.getModifiersTypeFromMappedType(t)) + if modifiersType.flags&TypeFlagsObject == 0 || t.objectFlags&ObjectFlagsMembersResolved != 0 { + return nil + } + lazy := &lazyMappedTable{ + typeParameter: typeParameter, + templateType: templateType, + modifiersType: modifiersType, + templateModifiers: getMappedTypeModifiers(t), + shouldLinkPropDeclarations: shouldLinkPropDeclarations, + members: make(ast.SymbolTable), + } + c.lazyMappedTables[t] = lazy + return lazy +} + +func (c *Checker) getLazyMappedTypeMember(t *Type, lazy *lazyMappedTable, name string) (member *ast.Symbol, ok bool) { + if existing, ok := lazy.members[name]; ok { + return existing, true + } + // Recursive lookups see no member, as they would while resolveMappedTypeMembers runs. + lazy.members[name] = nil + modifiersProp := c.getMemberOfStructuredType(lazy.modifiersType, name) + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + return t.AsStructuredType().members[name], true + } + if modifiersProp != nil && c.isNamedMember(modifiersProp, name) { + keyType := c.getLiteralTypeFromProperty(modifiersProp, TypeFlagsStringOrNumberLiteralOrUnique, false) + if !isTypeUsableAsPropertyName(keyType) || getPropertyNameFromType(keyType) != name { + delete(lazy.members, name) + return nil, false + } + member = c.newMappedTypeMember(t, lazy.modifiersType, lazy.templateModifiers, lazy.shouldLinkPropDeclarations, keyType, keyType, name) + } + lazy.members[name] = member + return member, true +} + +func (c *Checker) getLazyMappedTypeIndexInfos(t *Type, lazy *lazyMappedTable) []*IndexInfo { + if !lazy.indexInfosReady { + if lazy.resolving { + // Recursive requests see no index infos, as they would while resolveMappedTypeMembers runs. + return nil + } + lazy.resolving = true + defer func() { lazy.resolving = false }() + var indexInfos []*IndexInfo + for _, info := range c.getIndexInfosOfType(lazy.modifiersType) { + indexInfos = c.appendMappedTypeIndexInfo(indexInfos, t, lazy.modifiersType, lazy.typeParameter, lazy.templateType, lazy.templateModifiers, info.keyType, info.keyType) + } + if t.objectFlags&ObjectFlagsMembersResolved != 0 { + return t.AsStructuredType().indexInfos + } + lazy.indexInfos, lazy.indexInfosReady = indexInfos, true + } + return lazy.indexInfos +} + func (c *Checker) getTypeOfMappedSymbol(symbol *ast.Symbol) *Type { links := c.valueSymbolLinks.Get(symbol) if links.resolvedType == nil { @@ -22215,10 +22557,25 @@ func (c *Checker) isMappingOfSameObjectType(types []*Type) bool { func (c *Checker) somePropertyReducesToNever(t *Type) bool { // Collect declaration counts for each property across all constituent types of the intersection. + // A name that occurs in more than one constituent occurs in one other than skipped, so skipped + // only needs its members looked up, which may not resolve them. + types := t.Types() + skipped := slices.IndexFunc(types, func(t *Type) bool { + return t.objectFlags&(ObjectFlagsMapped|ObjectFlagsMembersResolved) == ObjectFlagsMapped || mayHaveLazyMembers(t) && t.flags&TypeFlagsObject != 0 + }) counts := make(map[string]int) - for _, t := range t.Types() { - for _, prop := range c.getPropertiesOfType(t) { - counts[prop.Name]++ + for i, t := range types { + if i != skipped { + for _, prop := range c.getPropertiesOfType(t) { + counts[prop.Name]++ + } + } + } + if skipped >= 0 { + for propName := range counts { + if c.getPropertyOfTypeEx(types[skipped], propName, true /*skipObjectFunctionPropertyAugment*/, false /*includeTypeOnlyMembers*/) != nil { + counts[propName]++ + } } } // Check if any property appears in more than one constituent type and reduces to 'never'. @@ -27828,7 +28185,7 @@ func (c *Checker) getPropertyNameFromIndex(indexType *Type, accessNode *ast.Node } func (c *Checker) isStringIndexSignatureOnlyTypeWorker(t *Type) bool { - return t.flags&TypeFlagsObject != 0 && !c.isGenericMappedType(t) && len(c.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) } @@ -31627,8 +31984,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. - 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/relater.go b/tsc/internal/checker/relater.go index 9d71d2504ca0c..c959dd60b249b 100644 --- a/tsc/internal/checker/relater.go +++ b/tsc/internal/checker/relater.go @@ -678,8 +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 { - 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 }) } 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/mappedTypeLazyMembers.errors.txt b/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.errors.txt new file mode 100644 index 0000000000000..fa492d2e56545 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.errors.txt @@ -0,0 +1,119 @@ +mappedTypeLazyMembers.ts(11,9): error TS2540: Cannot assign to 'name' because it is a read-only property. +mappedTypeLazyMembers.ts(12,9): error TS2339: Property 'missing' does not exist on type 'Partial'. +mappedTypeLazyMembers.ts(23,7): error TS2322: Type 'string[]' is not assignable to type 'number'. +mappedTypeLazyMembers.ts(33,7): error TS2322: Type 'Boxed' is not assignable to type 'Record'. + 'string' index signatures are incompatible. + Type '{ value: number; }' is not assignable to type '{ value: string; }'. + Types of property 'value' are incompatible. + Type 'number' is not assignable to type 'string'. +mappedTypeLazyMembers.ts(48,18): error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. +mappedTypeLazyMembers.ts(51,11): error TS2339: Property 'missing' does not exist on type 'Assertion'. +mappedTypeLazyMembers.ts(56,8): error TS2339: Property 'other' does not exist on type 'never'. + The intersection 'Kinded<{ kind: "a"; other: number; }>' was reduced to 'never' because property 'kind' has conflicting types in some constituents. +mappedTypeLazyMembers.ts(61,6): error TS2456: Type alias 'SelfMapped' circularly references itself. +mappedTypeLazyMembers.ts(61,27): error TS2313: Type parameter 'K' has a circular constraint. +mappedTypeLazyMembers.ts(65,21): error TS2313: Type parameter 'K' has a circular constraint. +mappedTypeLazyMembers.ts(66,11): error TS2310: Type 'Y' recursively references itself as a base type. + + +==== mappedTypeLazyMembers.ts (11 errors) ==== + // Member lookups, index signatures and never reduction on { [P in keyof T]: X } + // mapped types whose members aren't otherwise resolved. + + interface Options { + readonly name: string; + size?: number; + tags: string[]; + } + declare const partial: Partial; + const partial1 = partial.size; + partial.name = "x"; + ~~~~ +!!! error TS2540: Cannot assign to 'name' because it is a read-only property. + partial.missing; + ~~~~~~~ +!!! error TS2339: Property 'missing' does not exist on type 'Partial'. + + declare const required: Required; + const required1: number = required.size; + + declare const mutable: { -readonly [K in keyof Options]: Options[K] }; + mutable.name = "y"; + + type Boxed = { [K in keyof T]: { value: T[K] } }; + declare const boxed: Boxed; + const boxed1: string = boxed.name.value; + const boxed2: number = boxed.tags.value; + ~~~~~~ +!!! error TS2322: Type 'string[]' is not assignable to type 'number'. + + // Index signatures of the modifiers type become index signatures of the mapped type. + interface Dictionary { + [key: string]: number; + fixed: number; + } + declare const dict: Boxed; + const dict1 = dict.fixed.value; + const dict2 = dict.anything.value; + const dict3: Record = dict; + ~~~~~ +!!! error TS2322: Type 'Boxed' is not assignable to type 'Record'. +!!! error TS2322: 'string' index signatures are incompatible. +!!! error TS2322: Type '{ value: number; }' is not assignable to type '{ value: string; }'. +!!! error TS2322: Types of property 'value' are incompatible. +!!! error TS2322: Type 'number' is not assignable to type 'string'. + + // Assertion chains in the style of test frameworks, where the base type is an + // intersection of a mapped type and a call signature. + interface ChaiLike { + equal(value: unknown): void; + deep: ChaiLike; + } + type Chain = { [K in keyof A]: A[K] extends ChaiLike ? Assertion : A[K] } & ((message?: string) => Assertion); + interface Matchers { + toBe(value: T): void; + } + interface Assertion extends Chain, Matchers {} + declare function expect(value: T): Assertion; + expect(1).toBe(2); + expect("a").toBe(1); + ~ +!!! error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'. + expect(true).deep.equal(false); + expect(1)("message").toBe(3); + expect(1).missing; + ~~~~~~~ +!!! error TS2339: Property 'missing' does not exist on type 'Assertion'. + + // Intersections with mapped types that reduce to never. + type Kinded = { [K in keyof T]: T[K] } & { kind: "b" }; + declare const kinded: Kinded<{ kind: "a"; other: number }>; + kinded.other; + ~~~~~ +!!! error TS2339: Property 'other' does not exist on type 'never'. +!!! error TS2339: The intersection 'Kinded<{ kind: "a"; other: number; }>' was reduced to 'never' because property 'kind' has conflicting types in some constituents. + declare const compatible: Kinded<{ kind: "b"; other: number }>; + const compatible1: number = compatible.other; + + // Circular mapped types. + type SelfMapped = { [K in keyof SelfMapped]: string }; + ~~~~~~~~~~ +!!! error TS2456: Type alias 'SelfMapped' circularly references itself. + ~~~~~~~~~~~~~~~~ +!!! error TS2313: Type parameter 'K' has a circular constraint. + declare const selfMapped: SelfMapped; + selfMapped.anything; + + type X = { [K in keyof T]: string } & { b: string }; + ~~~~~~~ +!!! error TS2313: Type parameter 'K' has a circular constraint. +!!! related TS2751 mappedTypeLazyMembers.ts:66:11: Circularity originates in type at this location. + interface Y extends X { + ~ +!!! error TS2310: Type 'Y' recursively references itself as a base type. + a: ""; + } + declare const y: Y; + const y1 = y.a; + const y2 = y.b; + \ No newline at end of file diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.symbols b/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.symbols new file mode 100644 index 0000000000000..cb0fdfba24c5c --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.symbols @@ -0,0 +1,281 @@ +//// [tests/cases/compiler/mappedTypeLazyMembers.ts] //// + +=== mappedTypeLazyMembers.ts === +// Member lookups, index signatures and never reduction on { [P in keyof T]: X } +// mapped types whose members aren't otherwise resolved. + +interface Options { +>Options : Symbol(Options, Decl(mappedTypeLazyMembers.ts, 0, 0)) + + readonly name: string; +>name : Symbol(Options.name, Decl(mappedTypeLazyMembers.ts, 3, 19)) + + size?: number; +>size : Symbol(Options.size, Decl(mappedTypeLazyMembers.ts, 4, 26)) + + tags: string[]; +>tags : Symbol(Options.tags, Decl(mappedTypeLazyMembers.ts, 5, 18)) +} +declare const partial: Partial; +>partial : Symbol(partial, Decl(mappedTypeLazyMembers.ts, 8, 13)) +>Partial : Symbol(Partial, Decl(lib.es5.d.ts, --, --)) +>Options : Symbol(Options, Decl(mappedTypeLazyMembers.ts, 0, 0)) + +const partial1 = partial.size; +>partial1 : Symbol(partial1, Decl(mappedTypeLazyMembers.ts, 9, 5)) +>partial.size : Symbol(size, Decl(mappedTypeLazyMembers.ts, 4, 26)) +>partial : Symbol(partial, Decl(mappedTypeLazyMembers.ts, 8, 13)) +>size : Symbol(size, Decl(mappedTypeLazyMembers.ts, 4, 26)) + +partial.name = "x"; +>partial.name : Symbol(name, Decl(mappedTypeLazyMembers.ts, 3, 19)) +>partial : Symbol(partial, Decl(mappedTypeLazyMembers.ts, 8, 13)) +>name : Symbol(name, Decl(mappedTypeLazyMembers.ts, 3, 19)) + +partial.missing; +>partial : Symbol(partial, Decl(mappedTypeLazyMembers.ts, 8, 13)) + +declare const required: Required; +>required : Symbol(required, Decl(mappedTypeLazyMembers.ts, 13, 13)) +>Required : Symbol(Required, Decl(lib.es5.d.ts, --, --)) +>Options : Symbol(Options, Decl(mappedTypeLazyMembers.ts, 0, 0)) + +const required1: number = required.size; +>required1 : Symbol(required1, Decl(mappedTypeLazyMembers.ts, 14, 5)) +>required.size : Symbol(size, Decl(mappedTypeLazyMembers.ts, 4, 26)) +>required : Symbol(required, Decl(mappedTypeLazyMembers.ts, 13, 13)) +>size : Symbol(size, Decl(mappedTypeLazyMembers.ts, 4, 26)) + +declare const mutable: { -readonly [K in keyof Options]: Options[K] }; +>mutable : Symbol(mutable, Decl(mappedTypeLazyMembers.ts, 16, 13)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 16, 36)) +>Options : Symbol(Options, Decl(mappedTypeLazyMembers.ts, 0, 0)) +>Options : Symbol(Options, Decl(mappedTypeLazyMembers.ts, 0, 0)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 16, 36)) + +mutable.name = "y"; +>mutable.name : Symbol(name, Decl(mappedTypeLazyMembers.ts, 3, 19)) +>mutable : Symbol(mutable, Decl(mappedTypeLazyMembers.ts, 16, 13)) +>name : Symbol(name, Decl(mappedTypeLazyMembers.ts, 3, 19)) + +type Boxed = { [K in keyof T]: { value: T[K] } }; +>Boxed : Symbol(Boxed, Decl(mappedTypeLazyMembers.ts, 17, 19)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 19, 11)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 19, 19)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 19, 11)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 19, 11)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 19, 19)) + +declare const boxed: Boxed; +>boxed : Symbol(boxed, Decl(mappedTypeLazyMembers.ts, 20, 13)) +>Boxed : Symbol(Boxed, Decl(mappedTypeLazyMembers.ts, 17, 19)) +>Options : Symbol(Options, Decl(mappedTypeLazyMembers.ts, 0, 0)) + +const boxed1: string = boxed.name.value; +>boxed1 : Symbol(boxed1, Decl(mappedTypeLazyMembers.ts, 21, 5)) +>boxed.name.value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) +>boxed.name : Symbol(name, Decl(mappedTypeLazyMembers.ts, 3, 19)) +>boxed : Symbol(boxed, Decl(mappedTypeLazyMembers.ts, 20, 13)) +>name : Symbol(name, Decl(mappedTypeLazyMembers.ts, 3, 19)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) + +const boxed2: number = boxed.tags.value; +>boxed2 : Symbol(boxed2, Decl(mappedTypeLazyMembers.ts, 22, 5)) +>boxed.tags.value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) +>boxed.tags : Symbol(tags, Decl(mappedTypeLazyMembers.ts, 5, 18)) +>boxed : Symbol(boxed, Decl(mappedTypeLazyMembers.ts, 20, 13)) +>tags : Symbol(tags, Decl(mappedTypeLazyMembers.ts, 5, 18)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) + +// Index signatures of the modifiers type become index signatures of the mapped type. +interface Dictionary { +>Dictionary : Symbol(Dictionary, Decl(mappedTypeLazyMembers.ts, 22, 40)) + + [key: string]: number; +>key : Symbol(key, Decl(mappedTypeLazyMembers.ts, 26, 5)) + + fixed: number; +>fixed : Symbol(Dictionary.fixed, Decl(mappedTypeLazyMembers.ts, 26, 26)) +} +declare const dict: Boxed; +>dict : Symbol(dict, Decl(mappedTypeLazyMembers.ts, 29, 13)) +>Boxed : Symbol(Boxed, Decl(mappedTypeLazyMembers.ts, 17, 19)) +>Dictionary : Symbol(Dictionary, Decl(mappedTypeLazyMembers.ts, 22, 40)) + +const dict1 = dict.fixed.value; +>dict1 : Symbol(dict1, Decl(mappedTypeLazyMembers.ts, 30, 5)) +>dict.fixed.value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) +>dict.fixed : Symbol(fixed, Decl(mappedTypeLazyMembers.ts, 26, 26)) +>dict : Symbol(dict, Decl(mappedTypeLazyMembers.ts, 29, 13)) +>fixed : Symbol(fixed, Decl(mappedTypeLazyMembers.ts, 26, 26)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) + +const dict2 = dict.anything.value; +>dict2 : Symbol(dict2, Decl(mappedTypeLazyMembers.ts, 31, 5)) +>dict.anything.value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) +>dict : Symbol(dict, Decl(mappedTypeLazyMembers.ts, 29, 13)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 19, 35)) + +const dict3: Record = dict; +>dict3 : Symbol(dict3, Decl(mappedTypeLazyMembers.ts, 32, 5)) +>Record : Symbol(Record, Decl(lib.es5.d.ts, --, --)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 32, 29)) +>dict : Symbol(dict, Decl(mappedTypeLazyMembers.ts, 29, 13)) + +// Assertion chains in the style of test frameworks, where the base type is an +// intersection of a mapped type and a call signature. +interface ChaiLike { +>ChaiLike : Symbol(ChaiLike, Decl(mappedTypeLazyMembers.ts, 32, 54)) + + equal(value: unknown): void; +>equal : Symbol(ChaiLike.equal, Decl(mappedTypeLazyMembers.ts, 36, 20)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 37, 10)) + + deep: ChaiLike; +>deep : Symbol(ChaiLike.deep, Decl(mappedTypeLazyMembers.ts, 37, 32)) +>ChaiLike : Symbol(ChaiLike, Decl(mappedTypeLazyMembers.ts, 32, 54)) +} +type Chain = { [K in keyof A]: A[K] extends ChaiLike ? Assertion : A[K] } & ((message?: string) => Assertion); +>Chain : Symbol(Chain, Decl(mappedTypeLazyMembers.ts, 39, 1)) +>A : Symbol(A, Decl(mappedTypeLazyMembers.ts, 40, 11)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 40, 13)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 40, 22)) +>A : Symbol(A, Decl(mappedTypeLazyMembers.ts, 40, 11)) +>A : Symbol(A, Decl(mappedTypeLazyMembers.ts, 40, 11)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 40, 22)) +>ChaiLike : Symbol(ChaiLike, Decl(mappedTypeLazyMembers.ts, 32, 54)) +>Assertion : Symbol(Assertion, Decl(mappedTypeLazyMembers.ts, 43, 1)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 40, 13)) +>A : Symbol(A, Decl(mappedTypeLazyMembers.ts, 40, 11)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 40, 22)) +>message : Symbol(message, Decl(mappedTypeLazyMembers.ts, 40, 87)) +>Assertion : Symbol(Assertion, Decl(mappedTypeLazyMembers.ts, 43, 1)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 40, 13)) + +interface Matchers { +>Matchers : Symbol(Matchers, Decl(mappedTypeLazyMembers.ts, 40, 122)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 41, 19)) + + toBe(value: T): void; +>toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 42, 9)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 41, 19)) +} +interface Assertion extends Chain, Matchers {} +>Assertion : Symbol(Assertion, Decl(mappedTypeLazyMembers.ts, 43, 1)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 44, 20)) +>Chain : Symbol(Chain, Decl(mappedTypeLazyMembers.ts, 39, 1)) +>ChaiLike : Symbol(ChaiLike, Decl(mappedTypeLazyMembers.ts, 32, 54)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 44, 20)) +>Matchers : Symbol(Matchers, Decl(mappedTypeLazyMembers.ts, 40, 122)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 44, 20)) + +declare function expect(value: T): Assertion; +>expect : Symbol(expect, Decl(mappedTypeLazyMembers.ts, 44, 65)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 45, 24)) +>value : Symbol(value, Decl(mappedTypeLazyMembers.ts, 45, 27)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 45, 24)) +>Assertion : Symbol(Assertion, Decl(mappedTypeLazyMembers.ts, 43, 1)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 45, 24)) + +expect(1).toBe(2); +>expect(1).toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) +>expect : Symbol(expect, Decl(mappedTypeLazyMembers.ts, 44, 65)) +>toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) + +expect("a").toBe(1); +>expect("a").toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) +>expect : Symbol(expect, Decl(mappedTypeLazyMembers.ts, 44, 65)) +>toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) + +expect(true).deep.equal(false); +>expect(true).deep.equal : Symbol(equal, Decl(mappedTypeLazyMembers.ts, 36, 20)) +>expect(true).deep : Symbol(deep, Decl(mappedTypeLazyMembers.ts, 37, 32)) +>expect : Symbol(expect, Decl(mappedTypeLazyMembers.ts, 44, 65)) +>deep : Symbol(deep, Decl(mappedTypeLazyMembers.ts, 37, 32)) +>equal : Symbol(equal, Decl(mappedTypeLazyMembers.ts, 36, 20)) + +expect(1)("message").toBe(3); +>expect(1)("message").toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) +>expect : Symbol(expect, Decl(mappedTypeLazyMembers.ts, 44, 65)) +>toBe : Symbol(Matchers.toBe, Decl(mappedTypeLazyMembers.ts, 41, 23)) + +expect(1).missing; +>expect : Symbol(expect, Decl(mappedTypeLazyMembers.ts, 44, 65)) + +// Intersections with mapped types that reduce to never. +type Kinded = { [K in keyof T]: T[K] } & { kind: "b" }; +>Kinded : Symbol(Kinded, Decl(mappedTypeLazyMembers.ts, 50, 18)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 53, 12)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 53, 20)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 53, 12)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 53, 12)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 53, 20)) +>kind : Symbol(kind, Decl(mappedTypeLazyMembers.ts, 53, 45)) + +declare const kinded: Kinded<{ kind: "a"; other: number }>; +>kinded : Symbol(kinded, Decl(mappedTypeLazyMembers.ts, 54, 13)) +>Kinded : Symbol(Kinded, Decl(mappedTypeLazyMembers.ts, 50, 18)) +>kind : Symbol(kind, Decl(mappedTypeLazyMembers.ts, 54, 30)) +>other : Symbol(other, Decl(mappedTypeLazyMembers.ts, 54, 41)) + +kinded.other; +>kinded : Symbol(kinded, Decl(mappedTypeLazyMembers.ts, 54, 13)) + +declare const compatible: Kinded<{ kind: "b"; other: number }>; +>compatible : Symbol(compatible, Decl(mappedTypeLazyMembers.ts, 56, 13)) +>Kinded : Symbol(Kinded, Decl(mappedTypeLazyMembers.ts, 50, 18)) +>kind : Symbol(kind, Decl(mappedTypeLazyMembers.ts, 56, 34)) +>other : Symbol(other, Decl(mappedTypeLazyMembers.ts, 56, 45)) + +const compatible1: number = compatible.other; +>compatible1 : Symbol(compatible1, Decl(mappedTypeLazyMembers.ts, 57, 5)) +>compatible.other : Symbol(other, Decl(mappedTypeLazyMembers.ts, 56, 45)) +>compatible : Symbol(compatible, Decl(mappedTypeLazyMembers.ts, 56, 13)) +>other : Symbol(other, Decl(mappedTypeLazyMembers.ts, 56, 45)) + +// Circular mapped types. +type SelfMapped = { [K in keyof SelfMapped]: string }; +>SelfMapped : Symbol(SelfMapped, Decl(mappedTypeLazyMembers.ts, 57, 45)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 60, 21)) +>SelfMapped : Symbol(SelfMapped, Decl(mappedTypeLazyMembers.ts, 57, 45)) + +declare const selfMapped: SelfMapped; +>selfMapped : Symbol(selfMapped, Decl(mappedTypeLazyMembers.ts, 61, 13)) +>SelfMapped : Symbol(SelfMapped, Decl(mappedTypeLazyMembers.ts, 57, 45)) + +selfMapped.anything; +>selfMapped : Symbol(selfMapped, Decl(mappedTypeLazyMembers.ts, 61, 13)) + +type X = { [K in keyof T]: string } & { b: string }; +>X : Symbol(X, Decl(mappedTypeLazyMembers.ts, 62, 20)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 64, 7)) +>K : Symbol(K, Decl(mappedTypeLazyMembers.ts, 64, 15)) +>T : Symbol(T, Decl(mappedTypeLazyMembers.ts, 64, 7)) +>b : Symbol(b, Decl(mappedTypeLazyMembers.ts, 64, 42)) + +interface Y extends X { +>Y : Symbol(Y, Decl(mappedTypeLazyMembers.ts, 64, 55)) +>X : Symbol(X, Decl(mappedTypeLazyMembers.ts, 62, 20)) +>Y : Symbol(Y, Decl(mappedTypeLazyMembers.ts, 64, 55)) + + a: ""; +>a : Symbol(Y.a, Decl(mappedTypeLazyMembers.ts, 65, 26)) +} +declare const y: Y; +>y : Symbol(y, Decl(mappedTypeLazyMembers.ts, 68, 13)) +>Y : Symbol(Y, Decl(mappedTypeLazyMembers.ts, 64, 55)) + +const y1 = y.a; +>y1 : Symbol(y1, Decl(mappedTypeLazyMembers.ts, 69, 5)) +>y.a : Symbol(Y.a, Decl(mappedTypeLazyMembers.ts, 65, 26)) +>y : Symbol(y, Decl(mappedTypeLazyMembers.ts, 68, 13)) +>a : Symbol(Y.a, Decl(mappedTypeLazyMembers.ts, 65, 26)) + +const y2 = y.b; +>y2 : Symbol(y2, Decl(mappedTypeLazyMembers.ts, 70, 5)) +>y.b : Symbol(b, Decl(mappedTypeLazyMembers.ts, 64, 42)) +>y : Symbol(y, Decl(mappedTypeLazyMembers.ts, 68, 13)) +>b : Symbol(b, Decl(mappedTypeLazyMembers.ts, 64, 42)) + diff --git a/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.types b/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.types new file mode 100644 index 0000000000000..63acde7e9d394 --- /dev/null +++ b/tsc/testdata/baselines/reference/compiler/mappedTypeLazyMembers.types @@ -0,0 +1,243 @@ +//// [tests/cases/compiler/mappedTypeLazyMembers.ts] //// + +=== mappedTypeLazyMembers.ts === +// Member lookups, index signatures and never reduction on { [P in keyof T]: X } +// mapped types whose members aren't otherwise resolved. + +interface Options { + readonly name: string; +>name : string + + size?: number; +>size : number | undefined + + tags: string[]; +>tags : string[] +} +declare const partial: Partial; +>partial : Partial + +const partial1 = partial.size; +>partial1 : number | undefined +>partial.size : number | undefined +>partial : Partial +>size : number | undefined + +partial.name = "x"; +>partial.name = "x" : "x" +>partial.name : any +>partial : Partial +>name : any +>"x" : "x" + +partial.missing; +>partial.missing : any +>partial : Partial +>missing : any + +declare const required: Required; +>required : Required + +const required1: number = required.size; +>required1 : number +>required.size : number +>required : Required +>size : number + +declare const mutable: { -readonly [K in keyof Options]: Options[K] }; +>mutable : { name: string; size?: number; tags: string[]; } + +mutable.name = "y"; +>mutable.name = "y" : "y" +>mutable.name : string +>mutable : { name: string; size?: number; tags: string[]; } +>name : string +>"y" : "y" + +type Boxed = { [K in keyof T]: { value: T[K] } }; +>Boxed : Boxed +>value : T[K] + +declare const boxed: Boxed; +>boxed : Boxed + +const boxed1: string = boxed.name.value; +>boxed1 : string +>boxed.name.value : string +>boxed.name : { value: string; } +>boxed : Boxed +>name : { value: string; } +>value : string + +const boxed2: number = boxed.tags.value; +>boxed2 : number +>boxed.tags.value : string[] +>boxed.tags : { value: string[]; } +>boxed : Boxed +>tags : { value: string[]; } +>value : string[] + +// Index signatures of the modifiers type become index signatures of the mapped type. +interface Dictionary { + [key: string]: number; +>key : string + + fixed: number; +>fixed : number +} +declare const dict: Boxed; +>dict : Boxed + +const dict1 = dict.fixed.value; +>dict1 : number +>dict.fixed.value : number +>dict.fixed : { value: number; } +>dict : Boxed +>fixed : { value: number; } +>value : number + +const dict2 = dict.anything.value; +>dict2 : number +>dict.anything.value : number +>dict.anything : { value: number; } +>dict : Boxed +>anything : { value: number; } +>value : number + +const dict3: Record = dict; +>dict3 : Record +>value : string +>dict : Boxed + +// Assertion chains in the style of test frameworks, where the base type is an +// intersection of a mapped type and a call signature. +interface ChaiLike { + equal(value: unknown): void; +>equal : (value: unknown) => void +>value : unknown + + deep: ChaiLike; +>deep : ChaiLike +} +type Chain = { [K in keyof A]: A[K] extends ChaiLike ? Assertion : A[K] } & ((message?: string) => Assertion); +>Chain : Chain +>message : string | undefined + +interface Matchers { + toBe(value: T): void; +>toBe : (value: T) => void +>value : T +} +interface Assertion extends Chain, Matchers {} +declare function expect(value: T): Assertion; +>expect : (value: T) => Assertion +>value : T + +expect(1).toBe(2); +>expect(1).toBe(2) : void +>expect(1).toBe : (value: number) => void +>expect(1) : Assertion +>expect : (value: T) => Assertion +>1 : 1 +>toBe : (value: number) => void +>2 : 2 + +expect("a").toBe(1); +>expect("a").toBe(1) : void +>expect("a").toBe : (value: string) => void +>expect("a") : Assertion +>expect : (value: T) => Assertion +>"a" : "a" +>toBe : (value: string) => void +>1 : 1 + +expect(true).deep.equal(false); +>expect(true).deep.equal(false) : void +>expect(true).deep.equal : (value: unknown) => void +>expect(true).deep : Assertion +>expect(true) : Assertion +>expect : (value: T) => Assertion +>true : true +>deep : Assertion +>equal : (value: unknown) => void +>false : false + +expect(1)("message").toBe(3); +>expect(1)("message").toBe(3) : void +>expect(1)("message").toBe : (value: number) => void +>expect(1)("message") : Assertion +>expect(1) : Assertion +>expect : (value: T) => Assertion +>1 : 1 +>"message" : "message" +>toBe : (value: number) => void +>3 : 3 + +expect(1).missing; +>expect(1).missing : any +>expect(1) : Assertion +>expect : (value: T) => Assertion +>1 : 1 +>missing : any + +// Intersections with mapped types that reduce to never. +type Kinded = { [K in keyof T]: T[K] } & { kind: "b" }; +>Kinded : Kinded +>kind : "b" + +declare const kinded: Kinded<{ kind: "a"; other: number }>; +>kinded : never +>kind : "a" +>other : number + +kinded.other; +>kinded.other : any +>kinded : never +>other : any + +declare const compatible: Kinded<{ kind: "b"; other: number }>; +>compatible : Kinded<{ kind: "b"; other: number; }> +>kind : "b" +>other : number + +const compatible1: number = compatible.other; +>compatible1 : number +>compatible.other : number +>compatible : Kinded<{ kind: "b"; other: number; }> +>other : number + +// Circular mapped types. +type SelfMapped = { [K in keyof SelfMapped]: string }; +>SelfMapped : any + +declare const selfMapped: SelfMapped; +>selfMapped : any + +selfMapped.anything; +>selfMapped.anything : any +>selfMapped : any +>anything : any + +type X = { [K in keyof T]: string } & { b: string }; +>X : X +>b : string + +interface Y extends X { + a: ""; +>a : "" +} +declare const y: Y; +>y : Y + +const y1 = y.a; +>y1 : "" +>y.a : "" +>y : Y +>a : "" + +const y2 = y.b; +>y2 : string +>y.b : string +>y : Y +>b : string + 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/mappedTypeLazyMembers.ts b/tsc/testdata/tests/cases/compiler/mappedTypeLazyMembers.ts new file mode 100644 index 0000000000000..2137d06195a4f --- /dev/null +++ b/tsc/testdata/tests/cases/compiler/mappedTypeLazyMembers.ts @@ -0,0 +1,76 @@ +// @strict: true +// @target: esnext +// @noEmit: true +// @lib: esnext + +// Member lookups, index signatures and never reduction on { [P in keyof T]: X } +// mapped types whose members aren't otherwise resolved. + +interface Options { + readonly name: string; + size?: number; + tags: string[]; +} +declare const partial: Partial; +const partial1 = partial.size; +partial.name = "x"; +partial.missing; + +declare const required: Required; +const required1: number = required.size; + +declare const mutable: { -readonly [K in keyof Options]: Options[K] }; +mutable.name = "y"; + +type Boxed = { [K in keyof T]: { value: T[K] } }; +declare const boxed: Boxed; +const boxed1: string = boxed.name.value; +const boxed2: number = boxed.tags.value; + +// Index signatures of the modifiers type become index signatures of the mapped type. +interface Dictionary { + [key: string]: number; + fixed: number; +} +declare const dict: Boxed; +const dict1 = dict.fixed.value; +const dict2 = dict.anything.value; +const dict3: Record = dict; + +// Assertion chains in the style of test frameworks, where the base type is an +// intersection of a mapped type and a call signature. +interface ChaiLike { + equal(value: unknown): void; + deep: ChaiLike; +} +type Chain = { [K in keyof A]: A[K] extends ChaiLike ? Assertion : A[K] } & ((message?: string) => Assertion); +interface Matchers { + toBe(value: T): void; +} +interface Assertion extends Chain, Matchers {} +declare function expect(value: T): Assertion; +expect(1).toBe(2); +expect("a").toBe(1); +expect(true).deep.equal(false); +expect(1)("message").toBe(3); +expect(1).missing; + +// Intersections with mapped types that reduce to never. +type Kinded = { [K in keyof T]: T[K] } & { kind: "b" }; +declare const kinded: Kinded<{ kind: "a"; other: number }>; +kinded.other; +declare const compatible: Kinded<{ kind: "b"; other: number }>; +const compatible1: number = compatible.other; + +// Circular mapped types. +type SelfMapped = { [K in keyof SelfMapped]: string }; +declare const selfMapped: SelfMapped; +selfMapped.anything; + +type X = { [K in keyof T]: string } & { b: string }; +interface Y extends X { + a: ""; +} +declare const y: Y; +const y1 = y.a; +const y2 = y.b;