package checker import ( "maps" "slices" "strings" "github.com/microsoft/typescript-go/internal/ast" "github.com/microsoft/typescript-go/internal/astnav" "github.com/microsoft/typescript-go/internal/collections" "github.com/microsoft/typescript-go/internal/core" "github.com/microsoft/typescript-go/internal/debug" "github.com/microsoft/typescript-go/internal/printer" "github.com/microsoft/typescript-go/internal/scanner" ) func (c *Checker) GetSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { return c.getSymbolsInScope(location, meaning) } func (c *Checker) getSymbolsInScope(location *ast.Node, meaning ast.SymbolFlags) []*ast.Symbol { if location.Flags&ast.NodeFlagsInWithStatement != 0 { // We cannot answer semantic questions within a with block, do not proceed any further return nil } symbols := make(ast.SymbolTable) isStaticSymbol := false // Copy the given symbol into symbol tables if the symbol has the given meaning // and it doesn't already exists in the symbol table. copySymbol := func(symbol *ast.Symbol, meaning ast.SymbolFlags) { if symbol.CombinedLocalAndExportSymbolFlags()&meaning != 0 { id := symbol.Name // We will copy all symbol regardless of its reserved name because // symbolsToArray will check whether the key is a reserved name and // it will not copy symbol with reserved name to the array if _, ok := symbols[id]; !ok { symbols[id] = symbol } } } copySymbols := func(source ast.SymbolTable, meaning ast.SymbolFlags) { if meaning != 0 { for _, symbol := range source { copySymbol(symbol, meaning) } } } copyLocallyVisibleExportSymbols := func(source ast.SymbolTable, meaning ast.SymbolFlags) { if meaning != 0 { for _, symbol := range source { // Similar condition as in `resolveNameHelper` if ast.GetDeclarationOfKind(symbol, ast.KindExportSpecifier) == nil && ast.GetDeclarationOfKind(symbol, ast.KindNamespaceExport) == nil && symbol.Name != ast.InternalSymbolNameDefault { copySymbol(symbol, meaning) } } } } populateSymbols := func() { for location != nil { if canHaveLocals(location) && location.Locals() != nil && !ast.IsGlobalSourceFile(location) { copySymbols(location.Locals(), meaning) } switch location.Kind { case ast.KindSourceFile: if !ast.IsExternalModule(location.AsSourceFile()) { break } fallthrough case ast.KindModuleDeclaration: copyLocallyVisibleExportSymbols(c.getSymbolOfDeclaration(location).Exports, meaning&ast.SymbolFlagsModuleMember) case ast.KindEnumDeclaration: copySymbols(c.getSymbolOfDeclaration(location).Exports, meaning&ast.SymbolFlagsEnumMember) case ast.KindClassExpression: className := location.AsClassExpression().Name() if className != nil { copySymbol(location.Symbol(), meaning) } // this fall-through is necessary because we would like to handle // type parameter inside class expression similar to how we handle it in classDeclaration and interface Declaration. fallthrough case ast.KindClassDeclaration, ast.KindInterfaceDeclaration: // If we didn't come from static member of class or interface, // add the type parameters into the symbol table // (type parameters of classDeclaration/classExpression and interface are in member property of the symbol. // Note: that the memberFlags come from previous iteration. if !isStaticSymbol { copySymbols(c.getMembersOfSymbol(c.getSymbolOfDeclaration(location)), meaning&ast.SymbolFlagsType) } case ast.KindFunctionExpression: funcName := location.Name() if funcName != nil { copySymbol(location.Symbol(), meaning) } } if introducesArgumentsExoticObject(location) { copySymbol(c.argumentsSymbol, meaning) } isStaticSymbol = ast.IsStatic(location) location = location.Parent } copySymbols(c.globals, meaning) } populateSymbols() delete(symbols, ast.InternalSymbolNameThis) // Not a symbol, a keyword return symbolsToArray(symbols) } func (c *Checker) GetExportsOfModule(symbol *ast.Symbol) []*ast.Symbol { return symbolsToArray(c.getExportsOfModule(symbol)) } func (c *Checker) ForEachExportAndPropertyOfModule(moduleSymbol *ast.Symbol, cb func(*ast.Symbol, string)) { for key, exportedSymbol := range c.getExportsOfModule(moduleSymbol) { if !isReservedMemberName(key) { cb(exportedSymbol, key) } } exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) if exportEquals == moduleSymbol { return } typeOfSymbol := c.getTypeOfSymbol(exportEquals) if !c.shouldTreatPropertiesOfExternalModuleAsExports(typeOfSymbol) { return } // forEachPropertyOfType reducedType := c.getReducedApparentType(typeOfSymbol) if reducedType.flags&TypeFlagsStructuredType == 0 { return } for name, symbol := range c.resolveStructuredTypeMembers(reducedType).members { if c.isNamedMember(symbol, name) { cb(symbol, name) } } } func (c *Checker) IsValidPropertyAccess(node *ast.Node, propertyName string) bool { return c.isValidPropertyAccess(node, propertyName) } func (c *Checker) isValidPropertyAccess(node *ast.Node, propertyName string) bool { switch node.Kind { case ast.KindPropertyAccessExpression: return c.isValidPropertyAccessWithType(node, node.Expression().Kind == ast.KindSuperKeyword, propertyName, c.getWidenedType(c.checkExpression(node.Expression()))) case ast.KindQualifiedName: return c.isValidPropertyAccessWithType(node, false /*isSuper*/, propertyName, c.getWidenedType(c.checkExpression(node.AsQualifiedName().Left))) case ast.KindImportType: return c.isValidPropertyAccessWithType(node, false /*isSuper*/, propertyName, c.getTypeFromTypeNode(node)) } panic("Unexpected node kind in isValidPropertyAccess: " + node.Kind.String()) } func (c *Checker) isValidPropertyAccessWithType(node *ast.Node, isSuper bool, propertyName string, t *Type) bool { // Short-circuiting for improved performance. if IsTypeAny(t) { return true } prop := c.getPropertyOfType(t, propertyName) return prop != nil && c.isPropertyAccessible(node, isSuper, false /*isWrite*/, t, prop) } // Checks if an existing property access is valid for completions purposes. // node: a property access-like node where we want to check if we can access a property. // This node does not need to be an access of the property we are checking. // e.g. in completions, this node will often be an incomplete property access node, as in `foo.`. // Besides providing a location (i.e. scope) used to check property accessibility, we use this node for // computing whether this is a `super` property access. // type: the type whose property we are checking. // property: the accessed property's symbol. func (c *Checker) IsValidPropertyAccessForCompletions(node *ast.Node, t *Type, property *ast.Symbol) bool { return c.isPropertyAccessible( node, node.Kind == ast.KindPropertyAccessExpression && node.Expression().Kind == ast.KindSuperKeyword, false, /*isWrite*/ t, property, ) // Previously we validated the 'this' type of methods but this adversely affected performance. See #31377 for more context. } func (c *Checker) GetAllPossiblePropertiesOfTypes(types []*Type) []*ast.Symbol { unionType := c.getUnionType(types) if unionType.flags&TypeFlagsUnion == 0 { return c.getAugmentedPropertiesOfType(unionType) } props := make(ast.SymbolTable) for _, memberType := range types { augmentedProps := c.getAugmentedPropertiesOfType(memberType) for _, p := range augmentedProps { if _, ok := props[p.Name]; !ok { prop := c.createUnionOrIntersectionProperty(unionType, p.Name, false /*skipObjectFunctionPropertyAugment*/) // May be undefined if the property is private if prop != nil { props[p.Name] = prop } } } } return slices.Collect(maps.Values(props)) } func (c *Checker) IsUnknownSymbol(symbol *ast.Symbol) bool { return symbol == c.unknownSymbol } func (c *Checker) IsUndefinedSymbol(symbol *ast.Symbol) bool { return symbol == c.undefinedSymbol } func (c *Checker) IsArgumentsSymbol(symbol *ast.Symbol) bool { return symbol == c.argumentsSymbol } // Originally from services.ts func (c *Checker) GetNonOptionalType(t *Type) *Type { return c.removeOptionalTypeMarker(t) } func (c *Checker) GetStringIndexType(t *Type) *Type { return c.getIndexTypeOfType(t, c.stringType) } func (c *Checker) GetNumberIndexType(t *Type) *Type { return c.getIndexTypeOfType(t, c.numberType) } func (c *Checker) GetElementTypeOfArrayType(t *Type) *Type { return c.getElementTypeOfArrayType(t) } func (c *Checker) GetCallSignatures(t *Type) []*Signature { return c.getSignaturesOfType(t, SignatureKindCall) } func (c *Checker) GetConstructSignatures(t *Type) []*Signature { return c.getSignaturesOfType(t, SignatureKindConstruct) } func (c *Checker) GetApparentProperties(t *Type) []*ast.Symbol { return c.getAugmentedPropertiesOfType(t) } func (c *Checker) getAugmentedPropertiesOfType(t *Type) []*ast.Symbol { t = c.getApparentType(t) propsByName := createSymbolTable(c.getPropertiesOfType(t)) var functionType *Type if len(c.getSignaturesOfType(t, SignatureKindCall)) > 0 { functionType = c.globalCallableFunctionType } else if len(c.getSignaturesOfType(t, SignatureKindConstruct)) > 0 { functionType = c.globalNewableFunctionType } if propsByName == nil { propsByName = make(ast.SymbolTable) } if functionType != nil { for _, p := range c.getPropertiesOfType(functionType) { if _, ok := propsByName[p.Name]; !ok { propsByName[p.Name] = p } } } return c.getNamedMembers(propsByName, nil) } func (c *Checker) TryGetMemberInModuleExportsAndProperties(memberName string, moduleSymbol *ast.Symbol) *ast.Symbol { symbol := c.TryGetMemberInModuleExports(memberName, moduleSymbol) if symbol != nil { return symbol } exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) if exportEquals == moduleSymbol { return nil } t := c.getTypeOfSymbol(exportEquals) if c.shouldTreatPropertiesOfExternalModuleAsExports(t) { return c.getPropertyOfType(t, memberName) } return nil } func (c *Checker) TryGetMemberInModuleExports(memberName string, moduleSymbol *ast.Symbol) *ast.Symbol { symbolTable := c.getExportsOfModule(moduleSymbol) return symbolTable[memberName] } func (c *Checker) shouldTreatPropertiesOfExternalModuleAsExports(resolvedExternalModuleType *Type) bool { return resolvedExternalModuleType.flags&TypeFlagsPrimitive == 0 || resolvedExternalModuleType.objectFlags&ObjectFlagsClass != 0 || // `isArrayOrTupleLikeType` is too expensive to use in this auto-imports hot path. c.isArrayType(resolvedExternalModuleType) || isTupleType(resolvedExternalModuleType) } func (c *Checker) GetContextualType(node *ast.Expression, contextFlags ContextFlags) *Type { if contextFlags&ContextFlagsIgnoreNodeInferences != 0 { return runWithInferenceBlockedFromSourceNode(c, node, func() *Type { return c.getContextualType(node, contextFlags) }) } return c.getContextualType(node, contextFlags) } func runWithInferenceBlockedFromSourceNode[T any](c *Checker, node *ast.Node, fn func() T) T { containingCall := ast.FindAncestor(node, ast.IsCallLikeExpression) if containingCall != nil { toMarkSkip := node for { c.skipDirectInferenceNodes.Add(toMarkSkip) toMarkSkip = toMarkSkip.Parent if toMarkSkip == nil || toMarkSkip == containingCall { break } } } c.isInferencePartiallyBlocked = true result := runWithoutResolvedSignatureCaching(c, node, fn) c.isInferencePartiallyBlocked = false c.skipDirectInferenceNodes.Clear() return result } func GetResolvedSignatureForSignatureHelp(node *ast.Node, argumentCount int, c *Checker) (*Signature, []*Signature) { type result struct { signature *Signature candidates []*Signature } res := runWithoutResolvedSignatureCaching(c, node, func() result { signature, candidates := c.getResolvedSignatureWorker(node, CheckModeIsForSignatureHelp, argumentCount) return result{signature, candidates} }) return res.signature, res.candidates } func runWithoutResolvedSignatureCaching[T any](c *Checker, node *ast.Node, fn func() T) T { ancestorNode := ast.FindAncestor(node, ast.IsCallLikeOrFunctionLikeExpression) if ancestorNode != nil { cachedResolvedSignatures := make(map[*SignatureLinks]*Signature) cachedTypes := make(map[*ValueSymbolLinks]*Type) for ancestorNode != nil { signatureLinks := c.signatureLinks.Get(ancestorNode) cachedResolvedSignatures[signatureLinks] = signatureLinks.resolvedSignature signatureLinks.resolvedSignature = nil if ast.IsFunctionExpressionOrArrowFunction(ancestorNode) { symbolLinks := c.valueSymbolLinks.Get(c.getSymbolOfDeclaration(ancestorNode)) resolvedType := symbolLinks.resolvedType cachedTypes[symbolLinks] = resolvedType symbolLinks.resolvedType = nil } ancestorNode = ast.FindAncestor(ancestorNode.Parent, ast.IsCallLikeOrFunctionLikeExpression) } result := fn() for signatureLinks, resolvedSignature := range cachedResolvedSignatures { signatureLinks.resolvedSignature = resolvedSignature } for symbolLinks, resolvedType := range cachedTypes { symbolLinks.resolvedType = resolvedType } return result } return fn() } func (c *Checker) SkipAlias(symbol *ast.Symbol) *ast.Symbol { if symbol.Flags&ast.SymbolFlagsAlias != 0 { return c.GetAliasedSymbol(symbol) } return symbol } func (c *Checker) GetRootSymbols(symbol *ast.Symbol) []*ast.Symbol { roots := c.getImmediateRootSymbols(symbol) if len(roots) == 0 { return []*ast.Symbol{symbol} } var result []*ast.Symbol for _, root := range roots { result = append(result, c.GetRootSymbols(root)...) } return result } func (c *Checker) GetMappedTypeSymbolOfProperty(symbol *ast.Symbol) *ast.Symbol { if valueLinks := c.valueSymbolLinks.TryGet(symbol); valueLinks != nil { return valueLinks.containingType.symbol } return nil } func (c *Checker) getImmediateRootSymbols(symbol *ast.Symbol) []*ast.Symbol { if symbol.CheckFlags&ast.CheckFlagsSynthetic != 0 { return core.MapNonNil( c.valueSymbolLinks.Get(symbol).containingType.Types(), func(t *Type) *ast.Symbol { return c.getPropertyOfType(t, symbol.Name) }, ) } if symbol.Flags&ast.SymbolFlagsTransient != 0 { if c.spreadLinks.Has(symbol) { leftSpread := c.spreadLinks.Get(symbol).leftSpread rightSpread := c.spreadLinks.Get(symbol).rightSpread if leftSpread != nil { return []*ast.Symbol{leftSpread, rightSpread} } } if c.mappedSymbolLinks.Has(symbol) { syntheticOrigin := c.mappedSymbolLinks.Get(symbol).syntheticOrigin if syntheticOrigin != nil { return []*ast.Symbol{syntheticOrigin} } } target := c.tryGetTarget(symbol) if target != nil { return []*ast.Symbol{target} } } return nil } func (c *Checker) tryGetTarget(symbol *ast.Symbol) *ast.Symbol { var target *ast.Symbol next := symbol for { if c.valueSymbolLinks.Has(next) { next = c.valueSymbolLinks.Get(next).target } else if c.exportTypeLinks.Has(next) { next = c.exportTypeLinks.Get(next).target } else { next = nil } if next == nil { break } target = next } return target } func (c *Checker) GetExportSymbolOfSymbol(symbol *ast.Symbol) *ast.Symbol { return c.getMergedSymbol(core.IfElse(symbol.ExportSymbol != nil, symbol.ExportSymbol, symbol)) } func (c *Checker) GetExportSpecifierLocalTargetSymbol(node *ast.Node) *ast.Symbol { // node should be ExportSpecifier | Identifier switch node.Kind { case ast.KindExportSpecifier: if node.Parent.Parent.ModuleSpecifier() != nil { return c.getExternalModuleMember(node.Parent.Parent, node, false /*dontResolveAlias*/) } name := node.PropertyNameOrName() if name.Kind == ast.KindStringLiteral { // Skip for invalid syntax like this: export { "x" } return nil } return c.resolveEntityName(name, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) case ast.KindIdentifier: return c.resolveEntityName(node, ast.SymbolFlagsValue|ast.SymbolFlagsType|ast.SymbolFlagsNamespace|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) } panic("Unhandled case in getExportSpecifierLocalTargetSymbol, node should be ExportSpecifier | Identifier") } func (c *Checker) GetShorthandAssignmentValueSymbol(location *ast.Node) *ast.Symbol { if location != nil && location.Kind == ast.KindShorthandPropertyAssignment { return c.resolveEntityName(location.Name(), ast.SymbolFlagsValue|ast.SymbolFlagsAlias, true /*ignoreErrors*/, false, nil) } return nil } /** * Get symbols that represent parameter-property-declaration as parameter and as property declaration * @param parameter a parameterDeclaration node * @param parameterName a name of the parameter to get the symbols for. * @return a tuple of two symbols */ func (c *Checker) GetSymbolsOfParameterPropertyDeclaration(parameter *ast.Node /*ParameterPropertyDeclaration*/, parameterName string) (*ast.Symbol, *ast.Symbol) { constructorDeclaration := parameter.Parent classDeclaration := parameter.Parent.Parent parameterSymbol := c.getSymbol(constructorDeclaration.Locals(), parameterName, ast.SymbolFlagsValue) propertySymbol := c.getSymbol(c.getMembersOfSymbol(classDeclaration.Symbol()), parameterName, ast.SymbolFlagsValue) if parameterSymbol != nil && propertySymbol != nil { return parameterSymbol, propertySymbol } panic("There should exist two symbols, one as property declaration and one as parameter declaration") } // IsDeclarationUsed checks if an import declaration identifier is used in the source file. // This is primarily used for organizing imports to determine which imports can be removed. func (c *Checker) IsDeclarationUsed( sourceFile *ast.SourceFile, identifier *ast.Identifier, jsxElementsPresent bool, jsxModeNeedsExplicitImport bool, ) bool { if jsxElementsPresent && jsxModeNeedsExplicitImport { jsxNamespace := c.getJsxNamespace(sourceFile.AsNode()) jsxFragmentFactory := c.GetJsxFragmentFactory(sourceFile.AsNode()) identifierText := identifier.Text if identifierText == jsxNamespace { return true } if jsxFragmentFactory != "" && identifierText == jsxFragmentFactory { return true } } symbol := c.GetSymbolAtLocation(identifier.AsNode()) if symbol == nil { return true } return c.IsSymbolReferencedInFile(sourceFile, identifier, symbol) } // IsSymbolReferencedInFile checks if a symbol is referenced in the source file (besides its definition). // This is used as a quick check for whether a symbol is used at all in a file. func (c *Checker) IsSymbolReferencedInFile( sourceFile *ast.SourceFile, definition *ast.Identifier, symbol *ast.Symbol, ) bool { identifierText := definition.Text for _, token := range getPossibleSymbolReferenceNodes(sourceFile, identifierText, sourceFile.AsNode()) { if !ast.IsIdentifier(token) { continue } id := token.AsIdentifier() if id == definition || id.Text != identifierText { continue } refSymbol := c.GetSymbolAtLocation(token) if refSymbol == symbol { return true } if token.Parent != nil && token.Parent.Kind == ast.KindShorthandPropertyAssignment { shorthandSymbol := c.GetShorthandAssignmentValueSymbol(token.Parent) if shorthandSymbol == symbol { return true } } if token.Parent != nil && ast.IsExportSpecifier(token.Parent) { localSymbol := c.getLocalSymbolForExportSpecifier(token.AsIdentifier(), refSymbol, token.Parent.AsExportSpecifier()) if localSymbol == symbol { return true } } } return false } // GetReferencesToSymbolInFile returns all identifier nodes in the file that reference the given symbol. func (c *Checker) GetReferencesToSymbolInFile( sourceFile *ast.SourceFile, symbol *ast.Symbol, ) []*ast.Node { identifierText := symbol.Name var result []*ast.Node for _, token := range getPossibleSymbolReferenceNodes(sourceFile, identifierText, sourceFile.AsNode()) { if !ast.IsIdentifier(token) { continue } id := token.AsIdentifier() if id.Text != identifierText { continue } refSymbol := c.GetSymbolAtLocation(token) if refSymbol == symbol { result = append(result, token) continue } if token.Parent != nil && token.Parent.Kind == ast.KindShorthandPropertyAssignment { shorthandSymbol := c.GetShorthandAssignmentValueSymbol(token.Parent) if shorthandSymbol == symbol { result = append(result, token) continue } } if token.Parent != nil && ast.IsExportSpecifier(token.Parent) { localSymbol := c.getLocalSymbolForExportSpecifier(token.AsIdentifier(), refSymbol, token.Parent.AsExportSpecifier()) if localSymbol == symbol { result = append(result, token) continue } } } return result } func (c *Checker) getLocalSymbolForExportSpecifier(referenceLocation *ast.Identifier, referenceSymbol *ast.Symbol, exportSpecifier *ast.ExportSpecifier) *ast.Symbol { if isExportSpecifierAlias(referenceLocation, exportSpecifier) { if symbol := c.GetExportSpecifierLocalTargetSymbol(exportSpecifier.AsNode()); symbol != nil { return symbol } } return referenceSymbol } func isExportSpecifierAlias(referenceLocation *ast.Identifier, exportSpecifier *ast.ExportSpecifier) bool { debug.Assert(exportSpecifier.PropertyName == referenceLocation.AsNode() || exportSpecifier.Name() == referenceLocation.AsNode(), "referenceLocation is not export specifier name or property name") propertyName := exportSpecifier.PropertyName if propertyName != nil { // Given `export { foo as bar } [from "someModule"]`: It's an alias at `foo`, but at `bar` it's a new symbol. return propertyName == referenceLocation.AsNode() } else { // `export { foo } from "foo"` is a re-export. // `export { foo };` is not a re-export, it creates an alias for the local variable `foo`. return exportSpecifier.Parent.Parent.ModuleSpecifier() == nil } } func getPossibleSymbolReferenceNodes(sourceFile *ast.SourceFile, symbolName string, container *ast.Node) []*ast.Node { return core.MapNonNil(getPossibleSymbolReferencePositions(sourceFile, symbolName, container), func(pos int) *ast.Node { if referenceLocation := astnav.GetTouchingPropertyName(sourceFile, pos); referenceLocation != sourceFile.AsNode() { return referenceLocation } return nil }) } func getPossibleSymbolReferencePositions(sourceFile *ast.SourceFile, symbolName string, container *ast.Node) []int { positions := []int{} // TODO: Cache symbol existence for files to save text search // Also, need to make this work for unicode escapes. // Be resilient in the face of a symbol with no name or zero length name if symbolName == "" { return positions } text := sourceFile.Text() sourceLength := len(text) symbolNameLength := len(symbolName) if container == nil { container = sourceFile.AsNode() } position := strings.Index(text[container.Pos():], symbolName) endPos := container.End() for position >= 0 && position < endPos { // We found a match. Make sure it's not part of a larger word (i.e. the char // before and after it have to be a non-identifier char). endPosition := position + symbolNameLength if (position == 0 || !scanner.IsIdentifierPart(rune(text[position-1]))) && (endPosition == sourceLength || !scanner.IsIdentifierPart(rune(text[endPosition]))) { // Found a real match. Keep searching. positions = append(positions, position) } startIndex := position + symbolNameLength + 1 if startIndex > len(text) { break } if foundIndex := strings.Index(text[startIndex:], symbolName); foundIndex != -1 { position = startIndex + foundIndex } else { break } } return positions } func (c *Checker) GetTypeArgumentConstraint(node *ast.Node) *Type { if !ast.IsTypeNode(node) { return nil } return c.getTypeArgumentConstraint(node) } // getUninstantiatedSignatures gets generic signatures from the function's/constructor's type. func (c *Checker) getUninstantiatedSignatures(node *ast.Node) []*Signature { switch node.Kind { case ast.KindCallExpression, ast.KindDecorator: return c.getSignaturesOfType(c.getTypeOfExpression(node.Expression()), SignatureKindCall) case ast.KindNewExpression: return c.getSignaturesOfType(c.getTypeOfExpression(node.Expression()), SignatureKindConstruct) case ast.KindJsxSelfClosingElement, ast.KindJsxOpeningElement: if isJsxIntrinsicTagName(node.TagName()) { return nil } return c.getSignaturesOfType(c.getTypeOfExpression(node.TagName()), SignatureKindCall) case ast.KindTaggedTemplateExpression: return c.getSignaturesOfType(c.getTypeOfExpression(node.AsTaggedTemplateExpression().Tag), SignatureKindCall) case ast.KindBinaryExpression, ast.KindJsxOpeningFragment: return nil } return nil } func (c *Checker) getTypeParameterConstraintForPositionAcrossSignatures(signatures []*Signature, position int) *Type { var relevantConstraints []*Type for _, signature := range signatures { if position >= len(signature.typeParameters) { continue } relevantTypeParameter := signature.typeParameters[position] relevantConstraint := c.getConstraintOfTypeParameter(relevantTypeParameter) if relevantConstraint != nil { relevantConstraints = append(relevantConstraints, relevantConstraint) } } return c.getUnionType(relevantConstraints) } func (c *Checker) getTypeArgumentConstraint(node *ast.Node) *Type { var typeArgumentPosition int = -1 if ast.HasTypeArguments(node.Parent) { typeArgs := node.Parent.TypeArguments() for i, arg := range typeArgs { if arg == node { typeArgumentPosition = i break } } } if typeArgumentPosition >= 0 { // The node could be a type argument of a call, a `new` expression, a decorator, an // instantiation expression, or a generic type instantiation. if ast.IsCallLikeExpression(node.Parent) { return c.getTypeParameterConstraintForPositionAcrossSignatures( c.getUninstantiatedSignatures(node.Parent), typeArgumentPosition, ) } if ast.IsDecorator(node.Parent.Parent) { return c.getTypeParameterConstraintForPositionAcrossSignatures( c.getUninstantiatedSignatures(node.Parent.Parent), typeArgumentPosition, ) } if ast.IsExpressionWithTypeArguments(node.Parent) && ast.IsExpressionStatement(node.Parent.Parent) { uninstantiatedType := c.checkExpression(node.Parent.Expression()) callConstraint := c.getTypeParameterConstraintForPositionAcrossSignatures( c.getSignaturesOfType(uninstantiatedType, SignatureKindCall), typeArgumentPosition, ) constructConstraint := c.getTypeParameterConstraintForPositionAcrossSignatures( c.getSignaturesOfType(uninstantiatedType, SignatureKindConstruct), typeArgumentPosition, ) // An instantiation expression instantiates both call and construct signatures, so // if both exist type arguments must be assignable to both constraints. if constructConstraint.flags&TypeFlagsNever != 0 { return callConstraint } if callConstraint.flags&TypeFlagsNever != 0 { return constructConstraint } return c.getIntersectionType([]*Type{callConstraint, constructConstraint}) } if ast.IsTypeReferenceType(node.Parent) { typeParameters := c.getTypeParametersForTypeReferenceOrImport(node.Parent) if len(typeParameters) == 0 { return nil } if typeArgumentPosition >= len(typeParameters) { return nil } relevantTypeParameter := typeParameters[typeArgumentPosition] constraint := c.getConstraintOfTypeParameter(relevantTypeParameter) if constraint != nil { return c.instantiateType( constraint, newTypeMapper(typeParameters, c.getEffectiveTypeArguments(node.Parent, typeParameters)), ) } } } return nil } func (c *Checker) IsTypeInvalidDueToUnionDiscriminant(contextualType *Type, obj *ast.Node) bool { properties := obj.Properties() return core.Some(properties, func(property *ast.Node) bool { var nameType *Type propertyName := property.Name() if propertyName != nil { if ast.IsJsxNamespacedName(propertyName) { nameType = c.getStringLiteralType(propertyName.Text()) } else { nameType = c.getLiteralTypeFromPropertyName(propertyName) } } var name string if nameType != nil && isTypeUsableAsPropertyName(nameType) { name = getPropertyNameFromType(nameType) } var expected *Type if name != "" { expected = c.getTypeOfPropertyOfType(contextualType, name) } return expected != nil && isLiteralType(expected) && !c.isTypeAssignableTo(c.getTypeOfNode(property), expected) }) } // Unlike `getExportsOfModule`, this includes properties of an `export =` value. func (c *Checker) GetExportsAndPropertiesOfModule(moduleSymbol *ast.Symbol) []*ast.Symbol { exports := c.getExportsOfModuleAsArray(moduleSymbol) exportEquals := c.resolveExternalModuleSymbol(moduleSymbol, false /*dontResolveAlias*/) if exportEquals != moduleSymbol { t := c.getTypeOfSymbol(exportEquals) if c.shouldTreatPropertiesOfExternalModuleAsExports(t) { exports = append(exports, c.getPropertiesOfType(t)...) } } return exports } func (c *Checker) getExportsOfModuleAsArray(moduleSymbol *ast.Symbol) []*ast.Symbol { return symbolsToArray(c.getExportsOfModule(moduleSymbol)) } // Returns all the properties of the Jsx.IntrinsicElements interface. func (c *Checker) GetJsxIntrinsicTagNamesAt(location *ast.Node) []*ast.Symbol { intrinsics := c.getJsxType(JsxNames.IntrinsicElements, location) if intrinsics == nil { return nil } return c.GetPropertiesOfType(intrinsics) } func (c *Checker) GetContextualTypeForJsxAttribute(attribute *ast.JsxAttributeLike) *Type { return c.getContextualTypeForJsxAttribute(attribute, ContextFlagsNone) } func (c *Checker) GetConstantValue(node *ast.Node) any { if node.Kind == ast.KindEnumMember { return c.getEnumMemberValue(node).Value } if c.symbolNodeLinks.Get(node).resolvedSymbol == nil { c.checkExpressionCached(node) // ensure cached resolved symbol is set } symbol := c.symbolNodeLinks.Get(node).resolvedSymbol if symbol == nil && ast.IsEntityNameExpression(node) { symbol = c.resolveEntityName( node, ast.SymbolFlagsValue, true, /*ignoreErrors*/ false, /*dontResolveAlias*/ nil, /*location*/ ) } if symbol != nil && symbol.Flags&ast.SymbolFlagsEnumMember != 0 { // inline property\index accesses only for const enums member := symbol.ValueDeclaration if ast.IsEnumConst(member.Parent) { return c.getEnumMemberValue(member).Value } } return nil } func (c *Checker) getResolvedSignatureWorker(node *ast.Node, checkMode CheckMode, argumentCount int) (*Signature, []*Signature) { parsedNode := printer.NewEmitContext().ParseNode(node) c.apparentArgumentCount = &argumentCount candidatesOutArray := &[]*Signature{} var res *Signature if parsedNode != nil { res = c.getResolvedSignature(parsedNode, candidatesOutArray, checkMode) } c.apparentArgumentCount = nil return res, *candidatesOutArray } func (c *Checker) GetCandidateSignaturesForStringLiteralCompletions(call *ast.CallLikeExpression, editingArgument *ast.Node) []*Signature { // first, get candidates when inference is blocked from the source node. candidates := runWithInferenceBlockedFromSourceNode(c, editingArgument, func() []*Signature { _, blockedInferenceCandidates := c.getResolvedSignatureWorker(call, CheckModeNormal, 0) return blockedInferenceCandidates }) candidatesSet := collections.NewSetFromItems(candidates...) // next, get candidates where the source node is considered for inference. otherCandidates := runWithoutResolvedSignatureCaching(c, editingArgument, func() []*Signature { _, inferenceCandidates := c.getResolvedSignatureWorker(call, CheckModeNormal, 0) return inferenceCandidates }) for _, candidate := range otherCandidates { if candidatesSet.Has(candidate) { continue } candidates = append(candidates, candidate) } return candidates } // GetTypeAtPosition returns the type of a parameter at a given index in a signature. func (c *Checker) GetTypeAtPosition(s *Signature, pos int) *Type { return c.getTypeAtPosition(s, pos) } func (c *Checker) GetTypeParameterAtPosition(s *Signature, pos int) *Type { t := c.getTypeAtPosition(s, pos) if t.IsIndex() && isThisTypeParameter(t.AsIndexType().target) { constraint := c.getBaseConstraintOfType(t.AsIndexType().target) if constraint != nil { return c.getIndexType(constraint) } } return t } // GetContextualTypeForArrayLiteralAtPosition returns the contextual type for an element at the given position // in an array with the given contextual type. func (c *Checker) GetContextualTypeForArrayLiteralAtPosition(contextualArrayType *Type, arrayLiteral *ast.Node, position int) *Type { if contextualArrayType == nil { return nil } firstSpreadIndex, lastSpreadIndex := -1, -1 elementIndex := 0 elements := arrayLiteral.Elements() for i, elem := range elements { if elem.Pos() < position { elementIndex++ } if ast.IsSpreadElement(elem) { if firstSpreadIndex == -1 { firstSpreadIndex = i } lastSpreadIndex = i } } // The array may be incomplete, so we don't know its final length. return c.getContextualTypeForElementExpression( contextualArrayType, elementIndex, -1, /*length*/ firstSpreadIndex, lastSpreadIndex, ) } var knownGenericTypeNames = map[string]struct{}{ "Array": {}, "ArrayLike": {}, "ReadonlyArray": {}, "Promise": {}, "PromiseLike": {}, "Iterable": {}, "IterableIterator": {}, "AsyncIterable": {}, "Set": {}, "WeakSet": {}, "ReadonlySet": {}, "Map": {}, "WeakMap": {}, "ReadonlyMap": {}, "Partial": {}, "Required": {}, "Readonly": {}, "Pick": {}, "Omit": {}, "NonNullable": {}, } func isKnownGenericTypeName(name string) bool { _, exists := knownGenericTypeNames[name] return exists } func (c *Checker) GetFirstTypeArgumentFromKnownType(t *Type) *Type { if t.objectFlags&ObjectFlagsReference != 0 && t.symbol != nil && isKnownGenericTypeName(t.symbol.Name) { symbol := c.getGlobalSymbol(t.symbol.Name, ast.SymbolFlagsType, nil) if symbol != nil && symbol == t.Target().symbol { return core.FirstOrNil(c.getTypeArguments(t)) } } if t.alias != nil && isKnownGenericTypeName(t.alias.symbol.Name) { symbol := c.getGlobalSymbol(t.alias.symbol.Name, ast.SymbolFlagsType, nil) if symbol != nil && symbol == t.alias.symbol { return core.FirstOrNil(t.alias.typeArguments) } } return nil } // Gets all symbols for one property. Does not get symbols for every property. func (c *Checker) GetPropertySymbolsFromContextualType(node *ast.Node, contextualType *Type, unionSymbolOk bool) []*ast.Symbol { name := ast.GetTextOfPropertyName(node.Name()) if name == "" { return nil } if contextualType.flags&TypeFlagsUnion == 0 { if symbol := c.getPropertyOfType(contextualType, name); symbol != nil { return []*ast.Symbol{symbol} } return nil } filteredTypes := contextualType.Types() if ast.IsObjectLiteralExpression(node.Parent) || ast.IsJsxAttributes(node.Parent) { filteredTypes = core.Filter(filteredTypes, func(t *Type) bool { return !c.IsTypeInvalidDueToUnionDiscriminant(t, node.Parent) }) } discriminatedPropertySymbols := core.MapNonNil(filteredTypes, func(t *Type) *ast.Symbol { return c.getPropertyOfType(t, name) }) if unionSymbolOk && (len(discriminatedPropertySymbols) == 0 || len(discriminatedPropertySymbols) == len(contextualType.Types())) { if symbol := c.getPropertyOfType(contextualType, name); symbol != nil { return []*ast.Symbol{symbol} } } if len(filteredTypes) == 0 && len(discriminatedPropertySymbols) == 0 { // Bad discriminant -- do again without discriminating return core.MapNonNil(contextualType.Types(), func(t *Type) *ast.Symbol { return c.getPropertyOfType(t, name) }) } // by eliminating duplicates we might even end up with a single symbol // that helps with displaying better quick infos on properties of union types return core.Deduplicate(discriminatedPropertySymbols) } // Gets the property symbol corresponding to the property in destructuring assignment // 'property1' from // // for ( { property1: a } of elems) { // } // // 'property1' at location 'a' from: // // [a] = [ property1, property2 ] func (c *Checker) GetPropertySymbolOfDestructuringAssignment(location *ast.Node) *ast.Symbol { if ast.IsArrayLiteralOrObjectLiteralDestructuringPattern(location.Parent.Parent) { // Get the type of the object or array literal and then look for property of given name in the type if typeOfObjectLiteral := c.getTypeOfAssignmentPattern(location.Parent.Parent); typeOfObjectLiteral != nil { return c.getPropertyOfType(typeOfObjectLiteral, location.Text()) } } return nil } // Gets the type of object literal or array literal of destructuring assignment. // { a } from // // for ( { a } of elems) { // } // // [ a ] from // // [a] = [ some array ...] func (c *Checker) getTypeOfAssignmentPattern(expr *ast.Node) *Type { // If this is from "for of" // for ( { a } of elems) { // } if ast.IsForOfStatement(expr.Parent) { iteratedType := c.checkRightHandSideOfForOf(expr.Parent) return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) } // If this is from "for" initializer // for ({a } = elems[0];.....) { } if ast.IsBinaryExpression(expr.Parent) { iteratedType := c.getTypeOfExpression(expr.Parent.AsBinaryExpression().Right) return c.checkDestructuringAssignment(expr, core.OrElse(iteratedType, c.errorType), CheckModeNormal, false) } // If this is from nested object binding pattern // for ({ skills: { primary, secondary } } = multiRobot, i = 0; i < 1; i++) { if ast.IsPropertyAssignment(expr.Parent) { node := expr.Parent.Parent typeOfParentObjectLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType) propertyIndex := slices.Index(node.Properties(), expr.Parent) return c.checkObjectLiteralDestructuringPropertyAssignment(node, typeOfParentObjectLiteral, propertyIndex, nil, false) } // Array literal assignment - array destructuring pattern node := expr.Parent // [{ property1: p1, property2 }] = elems; typeOfArrayLiteral := core.OrElse(c.getTypeOfAssignmentPattern(node), c.errorType) elementType := core.OrElse(c.checkIteratedTypeOrElementType(IterationUseDestructuring, typeOfArrayLiteral, c.undefinedType, expr.Parent), c.errorType) return c.checkArrayLiteralDestructuringElementAssignment(node, typeOfArrayLiteral, slices.Index(node.Elements(), expr), elementType, CheckModeNormal) } func (c *Checker) GetSignatureFromDeclaration(node *ast.Node) *Signature { return c.getSignatureFromDeclaration(node) } // IsLibSymbolForHoverVerbosity returns true if a symbol is declared in a lib file. func (c *Checker) IsLibSymbolForHoverVerbosity(symbol *ast.Symbol) bool { if symbol == nil { return false } for _, decl := range symbol.Declarations { sf := ast.GetSourceFileOfNode(decl) if sf != nil && c.program.IsSourceFileDefaultLibrary(sf.Path()) { return true } } return false } // IsLibTypeForHoverVerbosity returns true if a type is declared in a lib file. // Don't expand types like Array or Promise, instead treating them as opaque. func (c *Checker) IsLibTypeForHoverVerbosity(t *Type) bool { var symbol *ast.Symbol if t.objectFlags&ObjectFlagsReference != 0 { symbol = t.Target().Symbol() } else { symbol = t.Symbol() } if c.IsLibSymbolForHoverVerbosity(symbol) { return true } return isTupleType(t) }