package checker import ( "iter" "math" "slices" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/ast" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/core" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/debug" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/diagnostics" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/jsnum" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/parser" "efprojects.com/kitten-ipc/kitcom/internal/tsgo/scanner" ) type JsxFlags uint32 const ( JsxFlagsNone JsxFlags = 0 JsxFlagsIntrinsicNamedElement JsxFlags = 1 << 0 // An element from a named property of the JSX.IntrinsicElements interface JsxFlagsIntrinsicIndexedElement JsxFlags = 1 << 1 // An element inferred from the string index signature of the JSX.IntrinsicElements interface JsxFlagsIntrinsicElement JsxFlags = JsxFlagsIntrinsicNamedElement | JsxFlagsIntrinsicIndexedElement ) type JsxReferenceKind int32 const ( JsxReferenceKindComponent JsxReferenceKind = iota JsxReferenceKindFunction JsxReferenceKindMixed ) type JsxElementLinks struct { jsxFlags JsxFlags // Flags for the JSX element resolvedJsxElementAttributesType *Type // Resolved element attributes type of a JSX opening-like element jsxNamespace *ast.Symbol // Resolved JSX namespace symbol for this node jsxImplicitImportContainer *ast.Symbol // Resolved module symbol the implicit JSX import of this file should refer to } var JsxNames = struct { JSX string IntrinsicElements string ElementClass string ElementAttributesPropertyNameContainer string ElementChildrenAttributeNameContainer string Element string ElementType string IntrinsicAttributes string IntrinsicClassAttributes string LibraryManagedAttributes string }{ JSX: "JSX", IntrinsicElements: "IntrinsicElements", ElementClass: "ElementClass", ElementAttributesPropertyNameContainer: "ElementAttributesProperty", ElementChildrenAttributeNameContainer: "ElementChildrenAttribute", Element: "Element", ElementType: "ElementType", IntrinsicAttributes: "IntrinsicAttributes", IntrinsicClassAttributes: "IntrinsicClassAttributes", LibraryManagedAttributes: "LibraryManagedAttributes", } var ReactNames = struct { Fragment string }{ Fragment: "Fragment", } func (c *Checker) checkJsxElement(node *ast.Node, checkMode CheckMode) *Type { c.checkNodeDeferred(node) return c.getJsxElementTypeAt(node) } func (c *Checker) checkJsxElementDeferred(node *ast.Node) { jsxElement := node.AsJsxElement() c.checkJsxOpeningLikeElementOrOpeningFragment(jsxElement.OpeningElement) // Perform resolution on the closing tag so that rename/go to definition/etc work if isJsxIntrinsicTagName(jsxElement.ClosingElement.TagName()) { c.getIntrinsicTagSymbol(jsxElement.ClosingElement) } else { c.checkExpression(jsxElement.ClosingElement.TagName()) } c.checkJsxChildren(node, CheckModeNormal) } func (c *Checker) checkJsxExpression(node *ast.Node, checkMode CheckMode) *Type { c.checkGrammarJsxExpression(node.AsJsxExpression()) if node.Expression() == nil { return c.errorType } t := c.checkExpressionEx(node.Expression(), checkMode) if node.AsJsxExpression().DotDotDotToken != nil && t != c.anyType && !c.isArrayType(t) { c.error(node, diagnostics.JSX_spread_child_must_be_an_array_type) } return t } func (c *Checker) checkJsxSelfClosingElement(node *ast.Node, checkMode CheckMode) *Type { c.checkNodeDeferred(node) return c.getJsxElementTypeAt(node) } func (c *Checker) checkJsxSelfClosingElementDeferred(node *ast.Node) { c.checkJsxOpeningLikeElementOrOpeningFragment(node) } func (c *Checker) checkJsxFragment(node *ast.Node) *Type { c.checkJsxOpeningLikeElementOrOpeningFragment(node.AsJsxFragment().OpeningFragment) // by default, jsx:'react' will use jsxFactory = React.createElement and jsxFragmentFactory = React.Fragment // if jsxFactory compiler option is provided, ensure jsxFragmentFactory compiler option or @jsxFrag pragma is provided too nodeSourceFile := ast.GetSourceFileOfNode(node) if c.compilerOptions.GetJSXTransformEnabled() && (c.compilerOptions.JsxFactory != "" || ast.GetPragmaFromSourceFile(nodeSourceFile, "jsx") != nil) && c.compilerOptions.JsxFragmentFactory == "" && ast.GetPragmaFromSourceFile(nodeSourceFile, "jsxfrag") == nil { message := core.IfElse(c.compilerOptions.JsxFactory != "", diagnostics.The_jsxFragmentFactory_compiler_option_must_be_provided_to_use_JSX_fragments_with_the_jsxFactory_compiler_option, diagnostics.An_jsxFrag_pragma_is_required_when_using_an_jsx_pragma_with_JSX_fragments) c.error(node, message) } c.checkJsxChildren(node, CheckModeNormal) t := c.getJsxElementTypeAt(node) return core.IfElse(c.isErrorType(t), c.anyType, t) } func (c *Checker) checkJsxAttributes(node *ast.Node, checkMode CheckMode) *Type { return c.createJsxAttributesTypeFromAttributesProperty(node.Parent, checkMode) } func (c *Checker) checkJsxOpeningLikeElementOrOpeningFragment(node *ast.Node) { isNodeOpeningLikeElement := ast.IsJsxOpeningLikeElement(node) if isNodeOpeningLikeElement { c.checkGrammarJsxElement(node) } c.checkJsxPreconditions(node) c.markJsxAliasReferenced(node) sig := c.getResolvedSignature(node, nil, CheckModeNormal) c.checkDeprecatedSignature(sig, node) if isNodeOpeningLikeElement { elementTypeConstraint := c.getJsxElementTypeTypeAt(node) if elementTypeConstraint != nil { tagName := node.TagName() var tagType *Type if isJsxIntrinsicTagName(tagName) { tagType = c.getStringLiteralType(tagName.Text()) } else { tagType = c.checkExpression(tagName) } var diags []*ast.Diagnostic if !c.checkTypeRelatedToEx(tagType, elementTypeConstraint, c.assignableRelation, tagName, diagnostics.Its_type_0_is_not_a_valid_JSX_element_type, &diags) { c.diagnostics.Add(ast.NewDiagnosticChain(diags[0], diagnostics.X_0_cannot_be_used_as_a_JSX_component, scanner.GetTextOfNode(tagName))) } } else { c.checkJsxReturnAssignableToAppropriateBound(c.getJsxReferenceKind(node), c.getReturnTypeOfSignature(sig), node) } } } func (c *Checker) checkJsxPreconditions(errorNode *ast.Node) { // Preconditions for using JSX if c.compilerOptions.Jsx == core.JsxEmitNone { c.error(errorNode, diagnostics.Cannot_use_JSX_unless_the_jsx_flag_is_provided) } if c.noImplicitAny && c.getJsxElementTypeAt(errorNode) == nil { c.error(errorNode, diagnostics.JSX_element_implicitly_has_type_any_because_the_global_type_JSX_Element_does_not_exist) } } func (c *Checker) checkJsxReturnAssignableToAppropriateBound(refKind JsxReferenceKind, elemInstanceType *Type, openingLikeElement *ast.Node) { var diags []*ast.Diagnostic switch refKind { case JsxReferenceKindFunction: sfcReturnConstraint := c.getJsxStatelessElementTypeAt(openingLikeElement) if sfcReturnConstraint != nil { c.checkTypeRelatedToEx(elemInstanceType, sfcReturnConstraint, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_return_type_0_is_not_a_valid_JSX_element, &diags) } case JsxReferenceKindComponent: classConstraint := c.getJsxElementClassTypeAt(openingLikeElement) if classConstraint != nil { // Issue an error if this return type isn't assignable to JSX.ElementClass, failing that c.checkTypeRelatedToEx(elemInstanceType, classConstraint, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_instance_type_0_is_not_a_valid_JSX_element, &diags) } default: sfcReturnConstraint := c.getJsxStatelessElementTypeAt(openingLikeElement) classConstraint := c.getJsxElementClassTypeAt(openingLikeElement) if sfcReturnConstraint == nil || classConstraint == nil { return } combined := c.getUnionType([]*Type{sfcReturnConstraint, classConstraint}) c.checkTypeRelatedToEx(elemInstanceType, combined, c.assignableRelation, openingLikeElement.TagName(), diagnostics.Its_element_type_0_is_not_a_valid_JSX_element, &diags) } if len(diags) != 0 { c.diagnostics.Add(ast.NewDiagnosticChain(diags[0], diagnostics.X_0_cannot_be_used_as_a_JSX_component, scanner.GetTextOfNode(openingLikeElement.TagName()))) } } func (c *Checker) inferJsxTypeArguments(node *ast.Node, signature *Signature, checkMode CheckMode, context *InferenceContext) []*Type { paramType := c.getEffectiveFirstArgumentForJsxSignature(signature, node) checkAttrType := c.checkExpressionWithContextualType(node.Attributes(), paramType, context, checkMode) c.inferTypes(context.inferences, checkAttrType, paramType, InferencePriorityNone, false) return c.getInferredTypes(context) } func (c *Checker) getContextualTypeForJsxExpression(node *ast.Node, contextFlags ContextFlags) *Type { switch { case ast.IsJsxAttributeLike(node.Parent): return c.getContextualType(node, contextFlags) case ast.IsJsxElement(node.Parent): return c.getContextualTypeForChildJsxExpression(node.Parent, node, contextFlags) } return nil } func (c *Checker) getContextualTypeForJsxAttribute(attribute *ast.Node, contextFlags ContextFlags) *Type { // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type // which is a type of the parameter of the signature we are trying out. // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName if ast.IsJsxAttribute(attribute) { attributesType := c.getApparentTypeOfContextualType(attribute.Parent, contextFlags) if attributesType == nil || IsTypeAny(attributesType) { return nil } return c.getTypeOfPropertyOfContextualType(attributesType, attribute.Name().Text()) } return c.getContextualType(attribute.Parent, contextFlags) } func (c *Checker) getContextualJsxElementAttributesType(node *ast.Node, contextFlags ContextFlags) *Type { if ast.IsJsxOpeningElement(node) && contextFlags != ContextFlagsCompletions { index := c.findContextualNode(node.Parent, contextFlags == ContextFlagsNone) if index >= 0 { // Contextually applied type is moved from attributes up to the outer jsx attributes so when walking up from the children they get hit // _However_ to hit them from the _attributes_ we must look for them here; otherwise we'll used the declared type // (as below) instead! return c.contextualInfos[index].t } } return c.getContextualTypeForArgumentAtIndex(node, 0) } func (c *Checker) getContextualTypeForChildJsxExpression(node *ast.Node, child *ast.JsxChild, contextFlags ContextFlags) *Type { attributesType := c.getApparentTypeOfContextualType(node.AsJsxElement().OpeningElement.Attributes(), contextFlags) // JSX expression is in children of JSX Element, we will look for an "children" attribute (we get the name from JSX.ElementAttributesProperty) jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) if !(attributesType != nil && !IsTypeAny(attributesType) && jsxChildrenPropertyName != ast.InternalSymbolNameMissing && jsxChildrenPropertyName != "") { return nil } realChildren := ast.GetSemanticJsxChildren(node.Children().Nodes) childIndex := slices.Index(realChildren, child) childFieldType := c.getTypeOfPropertyOfContextualType(attributesType, jsxChildrenPropertyName) if childFieldType == nil { return nil } if len(realChildren) == 1 { return childFieldType } return c.mapTypeEx(childFieldType, func(t *Type) *Type { if c.isArrayLikeType(t) { return c.getIndexedAccessType(t, c.getNumberLiteralType(jsnum.Number(childIndex))) } return t }, true /*noReductions*/) } func (c *Checker) discriminateContextualTypeByJSXAttributes(node *ast.Node, contextualType *Type) *Type { key := DiscriminatedContextualTypeKey{nodeId: ast.GetNodeId(node), typeId: contextualType.id} if discriminated := c.discriminatedContextualTypes[key]; discriminated != nil { return discriminated } jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) discriminantProperties := core.Filter(node.AsJsxAttributes().Properties.Nodes, func(p *ast.Node) bool { symbol := p.Symbol() if symbol == nil || !ast.IsJsxAttribute(p) { return false } initializer := p.Initializer() return (initializer == nil || c.isPossiblyDiscriminantValue(initializer)) && c.isDiscriminantProperty(contextualType, symbol.Name) }) discriminantMembers := core.Filter(c.getPropertiesOfType(contextualType), func(s *ast.Symbol) bool { if s.Flags&ast.SymbolFlagsOptional == 0 || node.Symbol() == nil || len(node.Symbol().Members) == 0 { return false } element := node.Parent.Parent if s.Name == jsxChildrenPropertyName && ast.IsJsxElement(element) && len(ast.GetSemanticJsxChildren(element.Children().Nodes)) != 0 { return false } return node.Symbol().Members[s.Name] == nil && c.isDiscriminantProperty(contextualType, s.Name) }) discriminator := &ObjectLiteralDiscriminator{c: c, props: discriminantProperties, members: discriminantMembers} discriminated := c.discriminateTypeByDiscriminableItems(contextualType, discriminator) c.discriminatedContextualTypes[key] = discriminated return discriminated } func (c *Checker) elaborateJsxComponents(node *ast.Node, source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { reportedError := false for _, prop := range node.AsJsxAttributes().Properties.Nodes { if !ast.IsJsxSpreadAttribute(prop) && !isHyphenatedJsxName(prop.Name().Text()) { nameType := c.getStringLiteralType(prop.Name().Text()) if nameType != nil && nameType.flags&TypeFlagsNever == 0 { reportedError = c.elaborateElement(source, target, relation, prop.Name(), prop.Initializer(), nameType, nil, diagnosticOutput) || reportedError } } } if ast.IsJsxOpeningElement(node.Parent) && ast.IsJsxElement(node.Parent.Parent) { containingElement := node.Parent.Parent // Containing JSXElement childrenPropName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(node)) if childrenPropName == ast.InternalSymbolNameMissing { childrenPropName = "children" } childrenNameType := c.getStringLiteralType(childrenPropName) childrenTargetType := c.getIndexedAccessType(target, childrenNameType) validChildren := ast.GetSemanticJsxChildren(containingElement.Children().Nodes) if len(validChildren) == 0 { return reportedError } moreThanOneRealChildren := len(validChildren) > 1 var arrayLikeTargetParts *Type var nonArrayLikeTargetParts *Type iterableType := c.getGlobalIterableType() if iterableType != c.emptyGenericType { anyIterable := c.createIterableType(c.anyType) arrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return c.isTypeAssignableTo(t, anyIterable) }) nonArrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return !c.isTypeAssignableTo(t, anyIterable) }) } else { arrayLikeTargetParts = c.filterType(childrenTargetType, c.isArrayOrTupleLikeType) nonArrayLikeTargetParts = c.filterType(childrenTargetType, func(t *Type) bool { return !c.isArrayOrTupleLikeType(t) }) } var invalidTextDiagnostic *diagnostics.Message getInvalidTextualChildDiagnostic := func() *diagnostics.Message { if invalidTextDiagnostic == nil { tagNameText := scanner.GetTextOfNode(node.Parent.TagName()) diagnostic := diagnostics.X_0_components_don_t_accept_text_as_child_elements_Text_in_JSX_has_the_type_string_but_the_expected_type_of_1_is_2 invalidTextDiagnostic = diagnostics.FormatMessage(diagnostic, tagNameText, childrenPropName, c.TypeToString(childrenTargetType)) } return invalidTextDiagnostic } if moreThanOneRealChildren { if arrayLikeTargetParts != c.neverType { realSource := c.createTupleType(c.checkJsxChildren(containingElement, CheckModeNormal)) children := c.generateJsxChildren(containingElement, getInvalidTextualChildDiagnostic) reportedError = c.elaborateIterableOrArrayLikeTargetElementwise(children, realSource, arrayLikeTargetParts, relation, diagnosticOutput) || reportedError } else if !c.isTypeRelatedTo(c.getIndexedAccessType(source, childrenNameType), childrenTargetType, relation) { // arity mismatch diag := c.error(containingElement.AsJsxElement().OpeningElement.TagName(), diagnostics.This_JSX_tag_s_0_prop_expects_a_single_child_of_type_1_but_multiple_children_were_provided, childrenPropName, c.TypeToString(childrenTargetType)) c.reportDiagnostic(diag, diagnosticOutput) reportedError = true } } else { if nonArrayLikeTargetParts != c.neverType { child := validChildren[0] e := c.getElaborationElementForJsxChild(child, childrenNameType, getInvalidTextualChildDiagnostic) if e.errorNode != nil { reportedError = c.elaborateElement(source, target, relation, e.errorNode, e.innerExpression, e.nameType, e.errorMessage, diagnosticOutput) || reportedError } } else if !c.isTypeRelatedTo(c.getIndexedAccessType(source, childrenNameType), childrenTargetType, relation) { // arity mismatch diag := c.error(containingElement.AsJsxElement().OpeningElement.TagName(), diagnostics.This_JSX_tag_s_0_prop_expects_type_1_which_requires_multiple_children_but_only_a_single_child_was_provided, childrenPropName, c.TypeToString(childrenTargetType)) c.reportDiagnostic(diag, diagnosticOutput) reportedError = true } } } return reportedError } type JsxElaborationElement struct { errorNode *ast.Node innerExpression *ast.Node nameType *Type errorMessage *diagnostics.Message } func (c *Checker) generateJsxChildren(node *ast.Node, getInvalidTextDiagnostic func() *diagnostics.Message) iter.Seq[JsxElaborationElement] { return func(yield func(JsxElaborationElement) bool) { memberOffset := 0 for i, child := range node.Children().Nodes { nameType := c.getNumberLiteralType(jsnum.Number(i - memberOffset)) e := c.getElaborationElementForJsxChild(child, nameType, getInvalidTextDiagnostic) if e.errorNode != nil { if !yield(e) { return } } else { memberOffset++ } } } } func (c *Checker) getElaborationElementForJsxChild(child *ast.Node, nameType *Type, getInvalidTextDiagnostic func() *diagnostics.Message) JsxElaborationElement { switch child.Kind { case ast.KindJsxExpression: // child is of the type of the expression return JsxElaborationElement{errorNode: child, innerExpression: child.Expression(), nameType: nameType} case ast.KindJsxText: if child.AsJsxText().ContainsOnlyTriviaWhiteSpaces { // Whitespace only jsx text isn't real jsx text return JsxElaborationElement{} } // child is a string return JsxElaborationElement{errorNode: child, innerExpression: nil, nameType: nameType, errorMessage: getInvalidTextDiagnostic()} case ast.KindJsxElement, ast.KindJsxSelfClosingElement, ast.KindJsxFragment: // child is of type JSX.Element return JsxElaborationElement{errorNode: child, innerExpression: child, nameType: nameType} } panic("Unhandled case in getElaborationElementForJsxChild") } func (c *Checker) elaborateIterableOrArrayLikeTargetElementwise(iterator iter.Seq[JsxElaborationElement], source *Type, target *Type, relation *Relation, diagnosticOutput *[]*ast.Diagnostic) bool { tupleOrArrayLikeTargetParts := c.filterType(target, c.isArrayOrTupleLikeType) nonTupleOrArrayLikeTargetParts := c.filterType(target, func(t *Type) bool { return !c.isArrayOrTupleLikeType(t) }) // If `nonTupleOrArrayLikeTargetParts` is not `never`, then that should mean `Iterable` is defined. var iterationType *Type if nonTupleOrArrayLikeTargetParts != c.neverType { iterationType = c.getIterationTypeOfIterable(IterationUseForOf, IterationTypeKindYield, nonTupleOrArrayLikeTargetParts, nil /*errorNode*/) } reportedError := false for e := range iterator { prop := e.errorNode next := e.innerExpression nameType := e.nameType targetPropType := iterationType var targetIndexedPropType *Type if tupleOrArrayLikeTargetParts != c.neverType { targetIndexedPropType = c.getBestMatchIndexedAccessTypeOrUndefined(source, tupleOrArrayLikeTargetParts, nameType) } if targetIndexedPropType != nil && targetIndexedPropType.flags&TypeFlagsIndexedAccess == 0 { if iterationType != nil { targetPropType = c.getUnionType([]*Type{iterationType, targetIndexedPropType}) } else { targetPropType = targetIndexedPropType } } if targetPropType == nil { continue } sourcePropType := c.getIndexedAccessTypeOrUndefined(source, nameType, AccessFlagsNone, nil, nil) if sourcePropType == nil { continue } propName := c.getPropertyNameFromIndex(nameType, nil /*accessNode*/) if !c.checkTypeRelatedTo(sourcePropType, targetPropType, relation, nil /*errorNode*/) { elaborated := next != nil && c.elaborateError(next, sourcePropType, targetPropType, relation, nil /*headMessage*/, diagnosticOutput) reportedError = true if !elaborated { // Issue error on the prop itself, since the prop couldn't elaborate the error. Use the expression type, if available. specificSource := sourcePropType if next != nil { specificSource = c.checkExpressionForMutableLocationWithContextualType(next, sourcePropType) } if c.exactOptionalPropertyTypes && c.isExactOptionalPropertyMismatch(specificSource, targetPropType) { diag := createDiagnosticForNode(prop, diagnostics.Type_0_is_not_assignable_to_type_1_with_exactOptionalPropertyTypes_Colon_true_Consider_adding_undefined_to_the_type_of_the_target, c.TypeToString(specificSource), c.TypeToString(targetPropType)) c.reportDiagnostic(diag, diagnosticOutput) } else { targetIsOptional := propName != ast.InternalSymbolNameMissing && core.OrElse(c.getPropertyOfType(tupleOrArrayLikeTargetParts, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0 sourceIsOptional := propName != ast.InternalSymbolNameMissing && core.OrElse(c.getPropertyOfType(source, propName), c.unknownSymbol).Flags&ast.SymbolFlagsOptional != 0 targetPropType = c.removeMissingType(targetPropType, targetIsOptional) sourcePropType = c.removeMissingType(sourcePropType, targetIsOptional && sourceIsOptional) result := c.checkTypeRelatedToEx(specificSource, targetPropType, relation, prop, e.errorMessage, diagnosticOutput) if result && specificSource != sourcePropType { // If for whatever reason the expression type doesn't yield an error, make sure we still issue an error on the sourcePropType c.checkTypeRelatedToEx(sourcePropType, targetPropType, relation, prop, e.errorMessage, diagnosticOutput) } } } } } return reportedError } func (c *Checker) getSuggestedSymbolForNonexistentJSXAttribute(name string, containingType *Type) *ast.Symbol { properties := c.getPropertiesOfType(containingType) var jsxSpecific *ast.Symbol switch name { case "for": jsxSpecific = core.Find(properties, func(x *ast.Symbol) bool { return ast.SymbolName(x) == "htmlFor" }) case "class": jsxSpecific = core.Find(properties, func(x *ast.Symbol) bool { return ast.SymbolName(x) == "className" }) } if jsxSpecific != nil { return jsxSpecific } return c.getSpellingSuggestionForName(name, properties, ast.SymbolFlagsValue) } func (c *Checker) getJSXFragmentType(node *ast.Node) *Type { // An opening fragment is required in order for `getJsxNamespace` to give the fragment factory links := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(node)) if links.jsxFragmentType != nil { return links.jsxFragmentType } jsxFragmentFactoryName := c.getJsxNamespace(node) // #38720/60122, allow null as jsxFragmentFactory shouldResolveFactoryReference := (c.compilerOptions.Jsx == core.JsxEmitReact || c.compilerOptions.JsxFragmentFactory != "") && jsxFragmentFactoryName != "null" if !shouldResolveFactoryReference { links.jsxFragmentType = c.anyType return links.jsxFragmentType } jsxFactorySymbol := c.getJsxNamespaceContainerForImplicitImport(node) if jsxFactorySymbol == nil { shouldModuleRefErr := c.compilerOptions.Jsx != core.JsxEmitPreserve && c.compilerOptions.Jsx != core.JsxEmitReactNative flags := ast.SymbolFlagsValue if !shouldModuleRefErr { flags &= ^ast.SymbolFlagsEnum } jsxFactorySymbol = c.resolveName(node, jsxFragmentFactoryName, flags, diagnostics.Using_JSX_fragments_requires_fragment_factory_0_to_be_in_scope_but_it_could_not_be_found, true /*isUse*/, false /*excludeGlobals*/) } if jsxFactorySymbol == nil { links.jsxFragmentType = c.errorType return links.jsxFragmentType } if jsxFactorySymbol.Name == ReactNames.Fragment { links.jsxFragmentType = c.getTypeOfSymbol(jsxFactorySymbol) return links.jsxFragmentType } resolvedAlias := jsxFactorySymbol if jsxFactorySymbol.Flags&ast.SymbolFlagsAlias != 0 { resolvedAlias = c.resolveAlias(jsxFactorySymbol) } reactExports := c.getExportsOfSymbol(resolvedAlias) typeSymbol := c.getSymbol(reactExports, ReactNames.Fragment, ast.SymbolFlagsBlockScopedVariable) if typeSymbol != nil { links.jsxFragmentType = c.getTypeOfSymbol(typeSymbol) } else { links.jsxFragmentType = c.errorType } return links.jsxFragmentType } func (c *Checker) resolveJsxOpeningLikeElement(node *ast.Node, candidatesOutArray *[]*Signature, checkMode CheckMode) *Signature { isJsxOpenFragment := ast.IsJsxOpeningFragment(node) var exprTypes *Type if !isJsxOpenFragment { if isJsxIntrinsicTagName(node.TagName()) { result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node) fakeSignature := c.createSignatureForJSXIntrinsic(node, result) c.checkTypeAssignableToAndOptionallyElaborate(c.checkExpressionWithContextualType(node.Attributes(), c.getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), nil /*inferenceContext*/, CheckModeNormal), result, node.TagName(), node.Attributes(), nil, nil) typeArguments := node.TypeArguments() if len(typeArguments) != 0 { c.checkSourceElements(typeArguments) c.diagnostics.Add(ast.NewDiagnostic(ast.GetSourceFileOfNode(node), node.TypeArgumentList().Loc, diagnostics.Expected_0_type_arguments_but_got_1, 0, len(typeArguments))) } return fakeSignature } exprTypes = c.checkExpression(node.TagName()) } else { exprTypes = c.getJSXFragmentType(node) } apparentType := c.getApparentType(exprTypes) if c.isErrorType(apparentType) { return c.resolveErrorCall(node) } signatures := c.getUninstantiatedJsxSignaturesOfType(exprTypes, node) if c.isUntypedFunctionCall(exprTypes, apparentType, len(signatures), 0 /*constructSignatures*/) { return c.resolveUntypedCall(node) } if len(signatures) == 0 { // We found no signatures at all, which is an error if isJsxOpenFragment { c.error(node, diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, scanner.GetTextOfNode(node)) } else { c.error(node.TagName(), diagnostics.JSX_element_type_0_does_not_have_any_construct_or_call_signatures, scanner.GetTextOfNode(node.TagName())) } return c.resolveErrorCall(node) } return c.resolveCall(node, signatures, candidatesOutArray, checkMode, SignatureFlagsNone, nil) } // Check if the given signature can possibly be a signature called by the JSX opening-like element. // @param node a JSX opening-like element we are trying to figure its call signature // @param signature a candidate signature we are trying whether it is a call signature // @param relation a relationship to check parameter and argument type func (c *Checker) checkApplicableSignatureForJsxCallLikeElement(node *ast.Node, signature *Signature, relation *Relation, checkMode CheckMode, reportErrors bool, diagnosticOutput *[]*ast.Diagnostic) bool { // Stateless function components can have maximum of three arguments: "props", "context", and "updater". // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props, // can be specified by users through attributes property. paramType := c.getEffectiveFirstArgumentForJsxSignature(signature, node) var attributesType *Type if ast.IsJsxOpeningFragment(node) { attributesType = c.createJsxAttributesTypeFromAttributesProperty(node, CheckModeNormal) } else { attributesType = c.checkExpressionWithContextualType(node.Attributes(), paramType, nil /*inferenceContext*/, checkMode) } var checkAttributesType *Type checkTagNameDoesNotExpectTooManyArguments := func() bool { if c.getJsxNamespaceContainerForImplicitImport(node) != nil { return true // factory is implicitly jsx/jsxdev - assume it fits the bill, since we don't strongly look for the jsx/jsxs/jsxDEV factory APIs anywhere else (at least not yet) } // We assume fragments have the correct arity since the node does not have attributes var tagType *Type if (ast.IsJsxOpeningElement(node) || ast.IsJsxSelfClosingElement(node)) && !(isJsxIntrinsicTagName(node.TagName()) || ast.IsJsxNamespacedName(node.TagName())) { tagType = c.checkExpression(node.TagName()) } if tagType == nil { return true } tagCallSignatures := c.getSignaturesOfType(tagType, SignatureKindCall) if len(tagCallSignatures) == 0 { return true } factory := c.getJsxFactoryEntity(node) if factory == nil { return true } factorySymbol := c.resolveEntityName(factory, ast.SymbolFlagsValue, true /*ignoreErrors*/, false /*dontResolveAlias*/, node) if factorySymbol == nil { return true } factoryType := c.getTypeOfSymbol(factorySymbol) callSignatures := c.getSignaturesOfType(factoryType, SignatureKindCall) if len(callSignatures) == 0 { return true } hasFirstParamSignatures := false maxParamCount := 0 // Check that _some_ first parameter expects a FC-like thing, and that some overload of the SFC expects an acceptable number of arguments for _, sig := range callSignatures { firstparam := c.getTypeAtPosition(sig, 0) signaturesOfParam := c.getSignaturesOfType(firstparam, SignatureKindCall) if len(signaturesOfParam) == 0 { continue } for _, paramSig := range signaturesOfParam { hasFirstParamSignatures = true if c.hasEffectiveRestParameter(paramSig) { return true // some signature has a rest param, so function components can have an arbitrary number of arguments } paramCount := c.getParameterCount(paramSig) if paramCount > maxParamCount { maxParamCount = paramCount } } } if !hasFirstParamSignatures { // Not a single signature had a first parameter which expected a signature - for back compat, and // to guard against generic factories which won't have signatures directly, do not error return true } absoluteMinArgCount := math.MaxInt for _, tagSig := range tagCallSignatures { tagRequiredArgCount := c.getMinArgumentCount(tagSig) if tagRequiredArgCount < absoluteMinArgCount { absoluteMinArgCount = tagRequiredArgCount } } if absoluteMinArgCount <= maxParamCount { return true // some signature accepts the number of arguments the function component provides } if reportErrors { tagName := node.TagName() // We will not report errors in this function for fragments, since we do not check them in this function diag := NewDiagnosticForNode(tagName, diagnostics.Tag_0_expects_at_least_1_arguments_but_the_JSX_factory_2_provides_at_most_3, entityNameToString(tagName), absoluteMinArgCount, entityNameToString(factory), maxParamCount) tagNameSymbol := c.getSymbolAtLocation(tagName, false) if tagNameSymbol != nil && tagNameSymbol.ValueDeclaration != nil { diag.AddRelatedInfo(NewDiagnosticForNode(tagNameSymbol.ValueDeclaration, diagnostics.X_0_is_declared_here, entityNameToString(tagName))) } c.reportDiagnostic(diag, diagnosticOutput) } return false } if checkMode&CheckModeSkipContextSensitive != 0 { checkAttributesType = c.getRegularTypeOfObjectLiteral(attributesType) } else { checkAttributesType = attributesType } if !checkTagNameDoesNotExpectTooManyArguments() { return false } var errorNode *ast.Node if reportErrors { if ast.IsJsxOpeningFragment(node) { errorNode = node } else { errorNode = node.TagName() } } var attributes *ast.Node if !ast.IsJsxOpeningFragment(node) { attributes = node.Attributes() } return c.checkTypeRelatedToAndOptionallyElaborate(checkAttributesType, paramType, relation, errorNode, attributes, nil, diagnosticOutput) } // Get attributes type of the JSX opening-like element. The result is from resolving "attributes" property of the opening-like element. // // @param openingLikeElement a JSX opening-like element // @param filter a function to remove attributes that will not participate in checking whether attributes are assignable // @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property. // @remarks Because this function calls getSpreadType, it needs to use the same checks as checkObjectLiteral, // which also calls getSpreadType. func (c *Checker) createJsxAttributesTypeFromAttributesProperty(openingLikeElement *ast.Node, checkMode CheckMode) *Type { var allAttributesTable ast.SymbolTable if c.strictNullChecks { allAttributesTable = make(ast.SymbolTable) } attributesTable := make(ast.SymbolTable) var attributesSymbol *ast.Symbol attributeParent := openingLikeElement spread := c.emptyJsxObjectType var hasSpreadAnyType bool var typeToIntersect *Type var explicitlySpecifyChildrenAttribute bool objectFlags := ObjectFlagsJsxAttributes createJsxAttributesType := func() *Type { objectFlags |= ObjectFlagsFreshLiteral result := c.newAnonymousType(attributesSymbol, attributesTable, nil, nil, nil) result.objectFlags |= objectFlags | ObjectFlagsObjectLiteral | ObjectFlagsContainsObjectOrArrayLiteral return result } jsxChildrenPropertyName := c.getJsxElementChildrenPropertyName(c.getJsxNamespaceAt(openingLikeElement)) isJsxOpenFragment := ast.IsJsxOpeningFragment(openingLikeElement) if !isJsxOpenFragment { attributes := openingLikeElement.Attributes() attributesSymbol = attributes.Symbol() attributeParent = attributes contextualType := c.getContextualType(attributes, ContextFlagsNone) // Create anonymous type from given attributes symbol table. // @param symbol a symbol of JsxAttributes containing attributes corresponding to attributesTable // @param attributesTable a symbol table of attributes property for _, attributeDecl := range attributes.AsJsxAttributes().Properties.Nodes { member := attributeDecl.Symbol() if ast.IsJsxAttribute(attributeDecl) { exprType := c.checkJsxAttribute(attributeDecl, checkMode) objectFlags |= exprType.objectFlags & ObjectFlagsPropagatingFlags attributeSymbol := c.newSymbol(ast.SymbolFlagsProperty|member.Flags, member.Name) attributeSymbol.Declarations = member.Declarations attributeSymbol.Parent = member.Parent if member.ValueDeclaration != nil { attributeSymbol.ValueDeclaration = member.ValueDeclaration } links := c.valueSymbolLinks.Get(attributeSymbol) links.resolvedType = exprType links.target = member attributesTable[attributeSymbol.Name] = attributeSymbol if allAttributesTable != nil { allAttributesTable[attributeSymbol.Name] = attributeSymbol } if attributeDecl.Name().Text() == jsxChildrenPropertyName { explicitlySpecifyChildrenAttribute = true } if contextualType != nil { prop := c.getPropertyOfType(contextualType, member.Name) if prop != nil && prop.Declarations != nil && c.isDeprecatedSymbol(prop) && ast.IsIdentifier(attributeDecl.Name()) { c.addDeprecatedSuggestion(attributeDecl.Name(), prop.Declarations, attributeDecl.Name().Text()) } } if contextualType != nil && checkMode&CheckModeInferential != 0 && checkMode&CheckModeSkipContextSensitive == 0 && c.isContextSensitive(attributeDecl) { inferenceContext := c.getInferenceContext(attributes) debug.AssertIsDefined(inferenceContext) // In CheckMode.Inferential we should always have an inference context inferenceNode := attributeDecl.Initializer().Expression() c.addIntraExpressionInferenceSite(inferenceContext, inferenceNode, exprType) } } else { debug.Assert(attributeDecl.Kind == ast.KindJsxSpreadAttribute) if len(attributesTable) != 0 { spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) attributesTable = make(ast.SymbolTable) } exprType := c.getReducedType(c.checkExpressionEx(attributeDecl.Expression(), checkMode&CheckModeInferential)) if IsTypeAny(exprType) { hasSpreadAnyType = true } if c.isValidSpreadType(exprType) { spread = c.getSpreadType(spread, exprType, attributesSymbol, objectFlags, false /*readonly*/) if allAttributesTable != nil { c.checkSpreadPropOverrides(exprType, allAttributesTable, attributeDecl) } } else { c.error(attributeDecl.Expression(), diagnostics.Spread_types_may_only_be_created_from_object_types) if typeToIntersect != nil { typeToIntersect = c.getIntersectionType([]*Type{typeToIntersect, exprType}) } else { typeToIntersect = exprType } } } } if !hasSpreadAnyType { if len(attributesTable) != 0 { spread = c.getSpreadType(spread, createJsxAttributesType(), attributesSymbol, objectFlags, false /*readonly*/) } } } parentHasSemanticJsxChildren := func(openingLikeElement *ast.Node) bool { // Handle children attribute parent := openingLikeElement.Parent if parent == nil { return false } var children []*ast.Node switch { case ast.IsJsxElement(parent): // We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement if parent.AsJsxElement().OpeningElement == openingLikeElement { children = parent.AsJsxElement().Children.Nodes } case ast.IsJsxFragment(parent): if parent.AsJsxFragment().OpeningFragment == openingLikeElement { children = parent.AsJsxFragment().Children.Nodes } } return len(ast.GetSemanticJsxChildren(children)) != 0 } if parentHasSemanticJsxChildren(openingLikeElement) { var childTypes []*Type = c.checkJsxChildren(openingLikeElement.Parent, checkMode) if !hasSpreadAnyType && jsxChildrenPropertyName != ast.InternalSymbolNameMissing && jsxChildrenPropertyName != "" { // Error if there is a attribute named "children" explicitly specified and children element. // This is because children element will overwrite the value from attributes. // Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread. if explicitlySpecifyChildrenAttribute { c.error(attributeParent, diagnostics.X_0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName) } var childrenContextualType *Type if ast.IsJsxOpeningElement(openingLikeElement) { if contextualType := c.getApparentTypeOfContextualType(openingLikeElement.Attributes(), ContextFlagsNone); contextualType != nil { childrenContextualType = c.getTypeOfPropertyOfContextualType(contextualType, jsxChildrenPropertyName) } } // If there are children in the body of JSX element, create dummy attribute "children" with the union of children types so that it will pass the attribute checking process childrenPropSymbol := c.newSymbol(ast.SymbolFlagsProperty, jsxChildrenPropertyName) links := c.valueSymbolLinks.Get(childrenPropSymbol) switch { case len(childTypes) == 1: links.resolvedType = childTypes[0] case childrenContextualType != nil && someType(childrenContextualType, c.isTupleLikeType): links.resolvedType = c.createTupleType(childTypes) default: links.resolvedType = c.createArrayType(c.getUnionType(childTypes)) } // Fake up a property declaration for the children childrenPropSymbol.ValueDeclaration = c.factory.NewPropertySignatureDeclaration(nil, c.factory.NewIdentifier(jsxChildrenPropertyName), nil /*postfixToken*/, nil /*type*/, nil /*initializer*/) childrenPropSymbol.ValueDeclaration.Parent = attributeParent childrenPropSymbol.ValueDeclaration.AsPropertySignatureDeclaration().Symbol = childrenPropSymbol childPropMap := make(ast.SymbolTable) childPropMap[jsxChildrenPropertyName] = childrenPropSymbol spread = c.getSpreadType(spread, c.newAnonymousType(attributesSymbol, childPropMap, nil, nil, nil), attributesSymbol, objectFlags, false /*readonly*/) } } if hasSpreadAnyType { return c.anyType } if typeToIntersect != nil { if spread != c.emptyJsxObjectType { return c.getIntersectionType([]*Type{typeToIntersect, spread}) } return typeToIntersect } if spread == c.emptyJsxObjectType { return createJsxAttributesType() } return spread } func (c *Checker) checkJsxAttribute(node *ast.Node, checkMode CheckMode) *Type { if node.Initializer() != nil { return c.checkExpressionForMutableLocation(node.Initializer(), checkMode) } // is sugar for return c.trueType } func (c *Checker) checkJsxChildren(node *ast.Node, checkMode CheckMode) []*Type { var childTypes []*Type for _, child := range node.Children().Nodes { // In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that // because then type of children property will have constituent of string type. if ast.IsJsxText(child) { if !child.AsJsxText().ContainsOnlyTriviaWhiteSpaces { childTypes = append(childTypes, c.stringType) } } else if ast.IsJsxExpression(child) && child.Expression() == nil { // empty jsx expressions don't *really* count as present children continue } else { childTypes = append(childTypes, c.checkExpressionForMutableLocation(child, checkMode)) } } return childTypes } func (c *Checker) getUninstantiatedJsxSignaturesOfType(elementType *Type, caller *ast.Node) []*Signature { if elementType.flags&TypeFlagsString != 0 { return []*Signature{c.anySignature} } if elementType.flags&TypeFlagsStringLiteral != 0 { intrinsicType := c.getIntrinsicAttributesTypeFromStringLiteralType(elementType, caller) if intrinsicType == nil { c.error(caller, diagnostics.Property_0_does_not_exist_on_type_1, getStringLiteralValue(elementType), "JSX."+JsxNames.IntrinsicElements) return nil } fakeSignature := c.createSignatureForJSXIntrinsic(caller, intrinsicType) return []*Signature{fakeSignature} } apparentElemType := c.getApparentType(elementType) // Resolve the signatures, preferring constructor signatures := c.getSignaturesOfType(apparentElemType, SignatureKindConstruct) if len(signatures) == 0 { // No construct signatures, try call signatures signatures = c.getSignaturesOfType(apparentElemType, SignatureKindCall) } if len(signatures) == 0 && apparentElemType.flags&TypeFlagsUnion != 0 { // If each member has some combination of new/call signatures; make a union signature list for those signatures = c.getUnionSignatures(core.Map(apparentElemType.Types(), func(t *Type) []*Signature { return c.getUninstantiatedJsxSignaturesOfType(t, caller) })) } return signatures } func (c *Checker) getEffectiveFirstArgumentForJsxSignature(signature *Signature, node *ast.Node) *Type { if ast.IsJsxOpeningFragment(node) || c.getJsxReferenceKind(node) != JsxReferenceKindComponent { return c.getJsxPropsTypeFromCallSignature(signature, node) } return c.getJsxPropsTypeFromClassType(signature, node) } func (c *Checker) getJsxPropsTypeFromCallSignature(sig *Signature, context *ast.Node) *Type { propsType := c.getTypeOfFirstParameterOfSignatureWithFallback(sig, c.unknownType) propsType = c.getJsxManagedAttributesFromLocatedAttributes(context, c.getJsxNamespaceAt(context), propsType) intrinsicAttribs := c.getJsxType(JsxNames.IntrinsicAttributes, context) if !c.isErrorType(intrinsicAttribs) { propsType = c.intersectTypes(intrinsicAttribs, propsType) } return propsType } func (c *Checker) getJsxPropsTypeFromClassType(sig *Signature, context *ast.Node) *Type { ns := c.getJsxNamespaceAt(context) forcedLookupLocation := c.getJsxElementPropertiesName(ns) var attributesType *Type switch forcedLookupLocation { case ast.InternalSymbolNameMissing: attributesType = c.getTypeOfFirstParameterOfSignatureWithFallback(sig, c.unknownType) case "": attributesType = c.getReturnTypeOfSignature(sig) default: attributesType = c.getJsxPropsTypeForSignatureFromMember(sig, forcedLookupLocation) if attributesType == nil && len(context.Attributes().AsJsxAttributes().Properties.Nodes) != 0 { // There is no property named 'props' on this instance type c.error(context, diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, forcedLookupLocation) } } if attributesType == nil { return c.unknownType } attributesType = c.getJsxManagedAttributesFromLocatedAttributes(context, ns, attributesType) if IsTypeAny(attributesType) { // Props is of type 'any' or unknown return attributesType } // Normal case -- add in IntrinsicClassAttributes and IntrinsicAttributes apparentAttributesType := attributesType intrinsicClassAttribs := c.getJsxType(JsxNames.IntrinsicClassAttributes, context) if !c.isErrorType(intrinsicClassAttribs) { typeParams := c.getLocalTypeParametersOfClassOrInterfaceOrTypeAlias(intrinsicClassAttribs.symbol) hostClassType := c.getReturnTypeOfSignature(sig) var libraryManagedAttributeType *Type if typeParams != nil { // apply JSX.IntrinsicClassAttributes inferredArgs := c.fillMissingTypeArguments([]*Type{hostClassType}, typeParams, c.getMinTypeArgumentCount(typeParams), ast.IsInJSFile(context)) libraryManagedAttributeType = c.instantiateType(intrinsicClassAttribs, newTypeMapper(typeParams, inferredArgs)) } else { libraryManagedAttributeType = intrinsicClassAttribs } apparentAttributesType = c.intersectTypes(libraryManagedAttributeType, apparentAttributesType) } intrinsicAttribs := c.getJsxType(JsxNames.IntrinsicAttributes, context) if !c.isErrorType(intrinsicAttribs) { apparentAttributesType = c.intersectTypes(intrinsicAttribs, apparentAttributesType) } return apparentAttributesType } func (c *Checker) getJsxPropsTypeForSignatureFromMember(sig *Signature, forcedLookupLocation string) *Type { if sig.composite != nil { // JSX Elements using the legacy `props`-field based lookup (eg, react class components) need to treat the `props` member as an input // instead of an output position when resolving the signature. We need to go back to the input signatures of the composite signature, // get the type of `props` on each return type individually, and then _intersect them_, rather than union them (as would normally occur // for a union signature). It's an unfortunate quirk of looking in the output of the signature for the type we want to use for the input. // The default behavior of `getTypeOfFirstParameterOfSignatureWithFallback` when no `props` member name is defined is much more sane. var results []*Type for _, signature := range sig.composite.signatures { instance := c.getReturnTypeOfSignature(signature) if IsTypeAny(instance) { return instance } propType := c.getTypeOfPropertyOfType(instance, forcedLookupLocation) if propType == nil { return nil } results = append(results, propType) } return c.getIntersectionType(results) // Same result for both union and intersection signatures } instanceType := c.getReturnTypeOfSignature(sig) if IsTypeAny(instanceType) { return instanceType } return c.getTypeOfPropertyOfType(instanceType, forcedLookupLocation) } func (c *Checker) getJsxManagedAttributesFromLocatedAttributes(context *ast.Node, ns *ast.Symbol, attributesType *Type) *Type { managedSym := c.getJsxLibraryManagedAttributes(ns) if managedSym != nil { ctorType := c.getStaticTypeOfReferencedJsxConstructor(context) result := c.instantiateAliasOrInterfaceWithDefaults(managedSym, []*Type{ctorType, attributesType}, ast.IsInJSFile(context)) if result != nil { return result } } return attributesType } func (c *Checker) instantiateAliasOrInterfaceWithDefaults(managedSym *ast.Symbol, typeArguments []*Type, inJavaScript bool) *Type { declaredManagedType := c.getDeclaredTypeOfSymbol(managedSym) // fetches interface type, or initializes symbol links type parmaeters if managedSym.Flags&ast.SymbolFlagsTypeAlias != 0 { params := c.typeAliasLinks.Get(managedSym).typeParameters if len(params) >= len(typeArguments) { args := c.fillMissingTypeArguments(typeArguments, params, len(typeArguments), inJavaScript) if len(args) == 0 { return declaredManagedType } return c.getTypeAliasInstantiation(managedSym, args, nil) } } if len(declaredManagedType.AsInterfaceType().TypeParameters()) >= len(typeArguments) { args := c.fillMissingTypeArguments(typeArguments, declaredManagedType.AsInterfaceType().TypeParameters(), len(typeArguments), inJavaScript) return c.createTypeReference(declaredManagedType, args) } return nil } func (c *Checker) getJsxLibraryManagedAttributes(jsxNamespace *ast.Symbol) *ast.Symbol { if jsxNamespace != nil { return c.getSymbol(jsxNamespace.Exports, JsxNames.LibraryManagedAttributes, ast.SymbolFlagsType) } return nil } func (c *Checker) getJsxElementTypeSymbol(jsxNamespace *ast.Symbol) *ast.Symbol { // JSX.ElementType [symbol] if jsxNamespace != nil { return c.getSymbol(jsxNamespace.Exports, JsxNames.ElementType, ast.SymbolFlagsType) } return nil } // e.g. "props" for React.d.ts, // or InternalSymbolNameMissing if ElementAttributesProperty doesn't exist (which means all // // non-intrinsic elements' attributes type is 'any'), // // or "" if it has 0 properties (which means every // // non-intrinsic elements' attributes type is the element instance type) func (c *Checker) getJsxElementPropertiesName(jsxNamespace *ast.Symbol) string { return c.getNameFromJsxElementAttributesContainer(JsxNames.ElementAttributesPropertyNameContainer, jsxNamespace) } func (c *Checker) getJsxElementChildrenPropertyName(jsxNamespace *ast.Symbol) string { if c.compilerOptions.Jsx == core.JsxEmitReactJSX || c.compilerOptions.Jsx == core.JsxEmitReactJSXDev { // In these JsxEmit modes the children property is fixed to 'children' return "children" } return c.getNameFromJsxElementAttributesContainer(JsxNames.ElementChildrenAttributeNameContainer, jsxNamespace) } // Look into JSX namespace and then look for container with matching name as nameOfAttribPropContainer. // Get a single property from that container if existed. Report an error if there are more than one property. // // @param nameOfAttribPropContainer a string of value JsxNames.ElementAttributesPropertyNameContainer or JsxNames.ElementChildrenAttributeNameContainer // // if other string is given or the container doesn't exist, return undefined. func (c *Checker) getNameFromJsxElementAttributesContainer(nameOfAttribPropContainer string, jsxNamespace *ast.Symbol) string { // JSX.ElementAttributesProperty | JSX.ElementChildrenAttribute [symbol] if jsxNamespace != nil { jsxElementAttribPropInterfaceSym := c.getSymbol(jsxNamespace.Exports, nameOfAttribPropContainer, ast.SymbolFlagsType) if jsxElementAttribPropInterfaceSym != nil { jsxElementAttribPropInterfaceType := c.getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym) propertiesOfJsxElementAttribPropInterface := c.getPropertiesOfType(jsxElementAttribPropInterfaceType) // Element Attributes has zero properties, so the element attributes type will be the class instance type if len(propertiesOfJsxElementAttribPropInterface) == 0 { return "" } if len(propertiesOfJsxElementAttribPropInterface) == 1 { return propertiesOfJsxElementAttribPropInterface[0].Name } if len(propertiesOfJsxElementAttribPropInterface) > 1 && len(jsxElementAttribPropInterfaceSym.Declarations) != 0 { // More than one property on ElementAttributesProperty is an error c.error(jsxElementAttribPropInterfaceSym.Declarations[0], diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, nameOfAttribPropContainer) } } } return ast.InternalSymbolNameMissing } func (c *Checker) getStaticTypeOfReferencedJsxConstructor(context *ast.Node) *Type { if ast.IsJsxOpeningFragment(context) { return c.getJSXFragmentType(context) } if isJsxIntrinsicTagName(context.TagName()) { result := c.getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context) fakeSignature := c.createSignatureForJSXIntrinsic(context, result) return c.getOrCreateTypeFromSignature(fakeSignature) } tagType := c.checkExpressionCached(context.TagName()) if tagType.flags&TypeFlagsStringLiteral != 0 { result := c.getIntrinsicAttributesTypeFromStringLiteralType(tagType, context) if result == nil { return c.errorType } fakeSignature := c.createSignatureForJSXIntrinsic(context, result) return c.getOrCreateTypeFromSignature(fakeSignature) } return tagType } func (c *Checker) getIntrinsicAttributesTypeFromStringLiteralType(t *Type, location *ast.Node) *Type { // If the elemType is a stringLiteral type, we can then provide a check to make sure that the string literal type is one of the Jsx intrinsic element type // For example: // var CustomTag: "h1" = "h1"; // Hello World intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, location) if !c.isErrorType(intrinsicElementsType) { stringLiteralTypeName := getStringLiteralValue(t) intrinsicProp := c.getPropertyOfType(intrinsicElementsType, stringLiteralTypeName) if intrinsicProp != nil { return c.getTypeOfSymbol(intrinsicProp) } indexSignatureType := c.getIndexTypeOfType(intrinsicElementsType, c.stringType) if indexSignatureType != nil { return indexSignatureType } return nil } // If we need to report an error, we already done so here. So just return any to prevent any more error downstream return c.anyType } func (c *Checker) getJsxReferenceKind(node *ast.Node) JsxReferenceKind { if isJsxIntrinsicTagName(node.TagName()) { return JsxReferenceKindMixed } tagType := c.getApparentType(c.checkExpression(node.TagName())) if len(c.getSignaturesOfType(tagType, SignatureKindConstruct)) != 0 { return JsxReferenceKindComponent } if len(c.getSignaturesOfType(tagType, SignatureKindCall)) != 0 { return JsxReferenceKindFunction } return JsxReferenceKindMixed } func (c *Checker) createSignatureForJSXIntrinsic(node *ast.Node, result *Type) *Signature { elementType := c.errorType if namespace := c.getJsxNamespaceAt(node); namespace != nil { if typeSymbol := c.getSymbol(c.getExportsOfSymbol(namespace), JsxNames.Element, ast.SymbolFlagsType); typeSymbol != nil { elementType = c.getDeclaredTypeOfSymbol(typeSymbol) } } // returnNode := typeSymbol && c.nodeBuilder.symbolToEntityName(typeSymbol, ast.SymbolFlagsType, node) // declaration := factory.createFunctionTypeNode(nil, []ParameterDeclaration{factory.createParameterDeclaration(nil, nil /*dotDotDotToken*/, "props", nil /*questionToken*/, c.nodeBuilder.typeToTypeNode(result, node))}, ifElse(returnNode != nil, factory.createTypeReferenceNode(returnNode, nil /*typeArguments*/), factory.createKeywordTypeNode(ast.KindAnyKeyword))) parameterSymbol := c.newSymbol(ast.SymbolFlagsFunctionScopedVariable, "props") c.valueSymbolLinks.Get(parameterSymbol).resolvedType = result return c.newSignature(SignatureFlagsNone, nil, nil, nil, []*ast.Symbol{parameterSymbol}, elementType, nil, 1) } // Get attributes type of the given intrinsic opening-like Jsx element by resolving the tag name. // The function is intended to be called from a function which has checked that the opening element is an intrinsic element. // @param node an intrinsic JSX opening-like element func (c *Checker) getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node *ast.Node) *Type { debug.Assert(isJsxIntrinsicTagName(node.TagName())) links := c.jsxElementLinks.Get(node) if links.resolvedJsxElementAttributesType != nil { return links.resolvedJsxElementAttributesType } symbol := c.getIntrinsicTagSymbol(node) if links.jsxFlags&JsxFlagsIntrinsicNamedElement != 0 { links.resolvedJsxElementAttributesType = core.OrElse(c.getTypeOfSymbol(symbol), c.errorType) return links.resolvedJsxElementAttributesType } if links.jsxFlags&JsxFlagsIntrinsicIndexedElement != 0 { indexInfo := c.getApplicableIndexInfoForName(c.getJsxType(JsxNames.IntrinsicElements, node), node.TagName().Text()) if indexInfo != nil { links.resolvedJsxElementAttributesType = indexInfo.valueType return links.resolvedJsxElementAttributesType } } links.resolvedJsxElementAttributesType = c.errorType return links.resolvedJsxElementAttributesType } // Looks up an intrinsic tag name and returns a symbol that either points to an intrinsic // property (in which case nodeLinks.jsxFlags will be IntrinsicNamedElement) or an intrinsic // string index signature (in which case nodeLinks.jsxFlags will be IntrinsicIndexedElement). // May also return unknownSymbol if both of these lookups fail. func (c *Checker) getIntrinsicTagSymbol(node *ast.Node) *ast.Symbol { links := c.symbolNodeLinks.Get(node) if links.resolvedSymbol != nil { return links.resolvedSymbol } intrinsicElementsType := c.getJsxType(JsxNames.IntrinsicElements, node) if !c.isErrorType(intrinsicElementsType) { // Property case tagName := node.TagName() if !ast.IsIdentifier(tagName) && !ast.IsJsxNamespacedName(tagName) { panic("Invalid tag name") } propName := tagName.Text() intrinsicProp := c.getPropertyOfType(intrinsicElementsType, propName) if intrinsicProp != nil { c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicNamedElement links.resolvedSymbol = intrinsicProp return links.resolvedSymbol } // Intrinsic string indexer case indexSymbol := c.getApplicableIndexSymbol(intrinsicElementsType, c.getStringLiteralType(propName)) if indexSymbol != nil { c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement links.resolvedSymbol = indexSymbol return links.resolvedSymbol } if c.getTypeOfPropertyOrIndexSignatureOfType(intrinsicElementsType, propName) != nil { c.jsxElementLinks.Get(node).jsxFlags |= JsxFlagsIntrinsicIndexedElement links.resolvedSymbol = intrinsicElementsType.symbol return links.resolvedSymbol } // Wasn't found c.error(node, diagnostics.Property_0_does_not_exist_on_type_1, tagName.Text(), "JSX."+JsxNames.IntrinsicElements) links.resolvedSymbol = c.unknownSymbol return links.resolvedSymbol } if c.noImplicitAny { c.error(node, diagnostics.JSX_element_implicitly_has_type_any_because_no_interface_JSX_0_exists, JsxNames.IntrinsicElements) } links.resolvedSymbol = c.unknownSymbol return links.resolvedSymbol } func (c *Checker) getJsxStatelessElementTypeAt(location *ast.Node) *Type { jsxElementType := c.getJsxElementTypeAt(location) if jsxElementType == nil { return nil } return c.getUnionType([]*Type{jsxElementType, c.nullType}) } func (c *Checker) getJsxElementClassTypeAt(location *ast.Node) *Type { t := c.getJsxType(JsxNames.ElementClass, location) if c.isErrorType(t) { return nil } return t } func (c *Checker) getJsxElementTypeAt(location *ast.Node) *Type { return c.getJsxType(JsxNames.Element, location) } func (c *Checker) getJsxElementTypeTypeAt(location *ast.Node) *Type { ns := c.getJsxNamespaceAt(location) if ns == nil { return nil } sym := c.getJsxElementTypeSymbol(ns) if sym == nil { return nil } t := c.instantiateAliasOrInterfaceWithDefaults(sym, nil, ast.IsInJSFile(location)) if t == nil || c.isErrorType(t) { return nil } return t } func (c *Checker) getJsxType(name string, location *ast.Node) *Type { if namespace := c.getJsxNamespaceAt(location); namespace != nil { if exports := c.getExportsOfSymbol(namespace); exports != nil { if typeSymbol := c.getSymbol(exports, name, ast.SymbolFlagsType); typeSymbol != nil { return c.getDeclaredTypeOfSymbol(typeSymbol) } } } return c.errorType } func (c *Checker) getJsxNamespaceAt(location *ast.Node) *ast.Symbol { var links *JsxElementLinks if location != nil { links = c.jsxElementLinks.Get(location) } if links != nil && links.jsxNamespace != nil && links.jsxNamespace != c.unknownSymbol { return links.jsxNamespace } if links == nil || links.jsxNamespace != c.unknownSymbol { resolvedNamespace := c.getJsxNamespaceContainerForImplicitImport(location) if resolvedNamespace == nil || resolvedNamespace == c.unknownSymbol { namespaceName := c.getJsxNamespace(location) resolvedNamespace = c.resolveName(location, namespaceName, ast.SymbolFlagsNamespace, nil /*nameNotFoundMessage*/, false /*isUse*/, false /*excludeGlobals*/) } if resolvedNamespace != nil { candidate := c.resolveSymbol(c.getSymbol(c.getExportsOfSymbol(c.resolveSymbol(resolvedNamespace)), JsxNames.JSX, ast.SymbolFlagsNamespace)) if candidate != nil && candidate != c.unknownSymbol { if links != nil { links.jsxNamespace = candidate } return candidate } } if links != nil { links.jsxNamespace = c.unknownSymbol } } // JSX global fallback s := c.resolveSymbol(c.getGlobalSymbol(JsxNames.JSX, ast.SymbolFlagsNamespace, nil /*diagnostic*/)) if s == c.unknownSymbol { return nil } return s } func (c *Checker) getJsxNamespace(location *ast.Node) string { if location != nil { file := ast.GetSourceFileOfNode(location) if file != nil { links := c.sourceFileLinks.Get(file) if ast.IsJsxOpeningFragment(location) { if links.localJsxFragmentNamespace != "" { return links.localJsxFragmentNamespace } jsxFragmentPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag") if jsxFragmentPragma != nil { links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragmentPragma.Args["factory"].Value) if links.localJsxFragmentFactory != nil { links.localJsxFragmentNamespace = ast.GetFirstIdentifier(links.localJsxFragmentFactory).Text() return links.localJsxFragmentNamespace } } entity := c.getJsxFragmentFactoryEntity(location) if entity != nil { links.localJsxFragmentFactory = entity links.localJsxFragmentNamespace = ast.GetFirstIdentifier(entity).Text() return links.localJsxFragmentNamespace } } else { localJsxNamespace := c.getLocalJsxNamespace(file) if localJsxNamespace != "" { links.localJsxNamespace = localJsxNamespace return links.localJsxNamespace } } } } if c._jsxNamespace == "" { c._jsxNamespace = "React" if c.compilerOptions.JsxFactory != "" { c._jsxFactoryEntity = c.parseIsolatedEntityName(c.compilerOptions.JsxFactory) if c._jsxFactoryEntity != nil { c._jsxNamespace = ast.GetFirstIdentifier(c._jsxFactoryEntity).Text() } } else if c.compilerOptions.ReactNamespace != "" { c._jsxNamespace = c.compilerOptions.ReactNamespace } } if c._jsxFactoryEntity == nil { c._jsxFactoryEntity = c.factory.NewQualifiedName(c.factory.NewIdentifier(c._jsxNamespace), c.factory.NewIdentifier("createElement")) } return c._jsxNamespace } func (c *Checker) getLocalJsxNamespace(file *ast.SourceFile) string { links := c.sourceFileLinks.Get(file) if links.localJsxNamespace != "" { return links.localJsxNamespace } jsxPragma := ast.GetPragmaFromSourceFile(file, "jsx") if jsxPragma != nil { links.localJsxFactory = c.parseIsolatedEntityName(jsxPragma.Args["factory"].Value) if links.localJsxFactory != nil { links.localJsxNamespace = ast.GetFirstIdentifier(links.localJsxFactory).Text() return links.localJsxNamespace } } return "" } func (c *Checker) getJsxFactoryEntity(location *ast.Node) *ast.Node { if location != nil { c.getJsxNamespace(location) if localJsxFactory := c.sourceFileLinks.Get(ast.GetSourceFileOfNode(location)).localJsxFactory; localJsxFactory != nil { return localJsxFactory } } return c._jsxFactoryEntity } func (c *Checker) getJsxFragmentFactoryEntity(location *ast.Node) *ast.EntityName { if location != nil { file := ast.GetSourceFileOfNode(location) if file != nil { links := c.sourceFileLinks.Get(file) if links.localJsxFragmentFactory != nil { return links.localJsxFragmentFactory } jsxFragPragma := ast.GetPragmaFromSourceFile(file, "jsxfrag") if jsxFragPragma != nil { links.localJsxFragmentFactory = c.parseIsolatedEntityName(jsxFragPragma.Args["factory"].Value) return links.localJsxFragmentFactory } } } if c.compilerOptions.JsxFragmentFactory != "" { return c.parseIsolatedEntityName(c.compilerOptions.JsxFragmentFactory) } return nil } func (c *Checker) parseIsolatedEntityName(name string) *ast.Node { result := parser.ParseIsolatedEntityName(name) if result != nil { markAsSynthetic(result) } return result } func markAsSynthetic(node *ast.Node) bool { node.Loc = core.NewTextRange(-1, -1) node.ForEachChild(markAsSynthetic) return false } func (c *Checker) getJsxNamespaceContainerForImplicitImport(location *ast.Node) *ast.Symbol { var file *ast.SourceFile var links *JsxElementLinks if location != nil { if file = ast.GetSourceFileOfNode(location); file != nil { links = c.jsxElementLinks.Get(file.AsNode()) } } if links != nil && links.jsxImplicitImportContainer != nil { return core.IfElse(links.jsxImplicitImportContainer == c.unknownSymbol, nil, links.jsxImplicitImportContainer) } moduleReference, specifier := c.getJSXRuntimeImportSpecifier(file) if moduleReference == "" { return nil } errorMessage := diagnostics.This_JSX_tag_requires_the_module_path_0_to_exist_but_none_could_be_found_Make_sure_you_have_types_for_the_appropriate_package_installed mod := c.resolveExternalModule(core.OrElse(specifier, location), moduleReference, errorMessage, location, false) var result *ast.Symbol if mod != nil && mod != c.unknownSymbol { result = c.getMergedSymbol(c.resolveSymbol(mod)) } if links != nil { links.jsxImplicitImportContainer = core.OrElse(result, c.unknownSymbol) } return result } func (c *Checker) getJSXRuntimeImportSpecifier(file *ast.SourceFile) (moduleReference string, specifier *ast.Node) { return c.program.GetJSXRuntimeImportSpecifier(file.Path()) }