@@ -16567,6 +16567,9 @@ namespace ts {
1656716567 // If we're already in the process of resolving the given signature, don't resolve again as
1656816568 // that could cause infinite recursion. Instead, return anySignature.
1656916569 const signature = getNodeLinks(callTarget).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(callTarget);
16570+ if (isJsxOpeningLikeElement(callTarget) && argIndex === 0) {
16571+ return getEffectiveFirstArgumentForJsxSignature(signature, callTarget);
16572+ }
1657016573 return getTypeAtPosition(signature, argIndex);
1657116574 }
1657216575
@@ -16934,7 +16937,11 @@ namespace ts {
1693416937 // (as below) instead!
1693516938 return node.parent.contextualType;
1693616939 }
16937- return getAttributesTypeFromJsxOpeningLikeElement(node);
16940+ return getContextualTypeForArgumentAtIndex(node, 0);
16941+ }
16942+
16943+ function getEffectiveFirstArgumentForJsxSignature(signature: Signature, node: JsxOpeningLikeElement) {
16944+ return isJsxStatelessFunctionReference(node) ? getJsxPropsTypeFromCallSignature(signature, node) : getJsxPropsTypeFromClassType(signature, node);
1693816945 }
1693916946
1694016947 function getJsxPropsTypeFromCallSignature(sig: Signature, context: JsxOpeningLikeElement) {
@@ -16952,23 +16959,42 @@ namespace ts {
1695216959 return isTypeAny(instanceType) ? instanceType : getTypeOfPropertyOfType(instanceType, forcedLookupLocation);
1695316960 }
1695416961
16962+ function getStaticTypeOfReferencedJsxConstructor(context: JsxOpeningLikeElement) {
16963+ if (isJsxIntrinsicIdentifier(context.tagName)) {
16964+ const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(context);
16965+ const fakeSignature = createSignatureForJSXIntrinsic(context, result);
16966+ return getOrCreateTypeFromSignature(fakeSignature);
16967+ }
16968+ const tagType = checkExpressionCached(context.tagName);
16969+ if (tagType.flags & TypeFlags.StringLiteral) {
16970+ const result = getIntrinsicAttributesTypeFromStringLiteralType(tagType as StringLiteralType, context);
16971+ if (!result) {
16972+ return errorType;
16973+ }
16974+ const fakeSignature = createSignatureForJSXIntrinsic(context, result);
16975+ return getOrCreateTypeFromSignature(fakeSignature);
16976+ }
16977+ return tagType;
16978+ }
16979+
1695516980 function getJsxManagedAttributesFromLocatedAttributes(context: JsxOpeningLikeElement, ns: Symbol, attributesType: Type) {
1695616981 const managedSym = getJsxLibraryManagedAttributes(ns);
1695716982 if (managedSym) {
1695816983 const declaredManagedType = getDeclaredTypeOfSymbol(managedSym);
16984+ const ctorType = getStaticTypeOfReferencedJsxConstructor(context);
1695916985 if (length((declaredManagedType as GenericType).typeParameters) >= 2) {
16960- const args = fillMissingTypeArguments([checkExpressionCached(context.tagName) , attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
16986+ const args = fillMissingTypeArguments([ctorType , attributesType], (declaredManagedType as GenericType).typeParameters, 2, isInJSFile(context));
1696116987 return createTypeReference((declaredManagedType as GenericType), args);
1696216988 }
1696316989 else if (length(declaredManagedType.aliasTypeArguments) >= 2) {
16964- const args = fillMissingTypeArguments([checkExpressionCached(context.tagName) , attributesType], declaredManagedType.aliasTypeArguments!, 2, isInJSFile(context));
16990+ const args = fillMissingTypeArguments([ctorType , attributesType], declaredManagedType.aliasTypeArguments!, 2, isInJSFile(context));
1696516991 return getTypeAliasInstantiation(declaredManagedType.aliasSymbol!, args);
1696616992 }
1696716993 }
1696816994 return attributesType;
1696916995 }
1697016996
16971- function getJsxPropsTypeFromClassType(sig: Signature, isJs: boolean, context: JsxOpeningLikeElement) {
16997+ function getJsxPropsTypeFromClassType(sig: Signature, context: JsxOpeningLikeElement) {
1697216998 const ns = getJsxNamespaceAt(context);
1697316999 const forcedLookupLocation = getJsxElementPropertiesName(ns);
1697417000 let attributesType = forcedLookupLocation === undefined
@@ -17003,7 +17029,7 @@ namespace ts {
1700317029 const hostClassType = getReturnTypeOfSignature(sig);
1700417030 apparentAttributesType = intersectTypes(
1700517031 typeParams
17006- ? createTypeReference(<GenericType>intrinsicClassAttribs, fillMissingTypeArguments([hostClassType], typeParams, getMinTypeArgumentCount(typeParams), isJs ))
17032+ ? createTypeReference(<GenericType>intrinsicClassAttribs, fillMissingTypeArguments([hostClassType], typeParams, getMinTypeArgumentCount(typeParams), isInJSFile(context) ))
1700717033 : intrinsicClassAttribs,
1700817034 apparentAttributesType
1700917035 );
@@ -17843,50 +17869,6 @@ namespace ts {
1784317869 return anyType;
1784417870 }
1784517871
17846- /**
17847- * Resolve attributes type of the given opening-like element. The attributes type is a type of attributes associated with the given elementType.
17848- * For instance:
17849- * declare function Foo(attr: { p1: string}): JSX.Element;
17850- * <Foo p1={10} />; // This function will try resolve "Foo" and return an attributes type of "Foo" which is "{ p1: string }"
17851- *
17852- * The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element..
17853- * This function will try to resolve custom JSX attributes type in following order: string literal, stateless function, and stateful component
17854- *
17855- * @param openingLikeElement a non-intrinsic JSXOPeningLikeElement
17856- * @param elementType an instance type of the given opening-like element. If undefined, the function will check type openinglikeElement's tagname.
17857- * @return attributes type if able to resolve the type of node
17858- * anyType if there is no type ElementAttributesProperty or there is an error
17859- * emptyObjectType if there is no "prop" in the element instance type
17860- */
17861- function resolveCustomJsxElementAttributesType(openingLikeElement: JsxOpeningLikeElement, elementType: Type): Type {
17862- if (elementType.flags & TypeFlags.Union) {
17863- const types = (elementType as UnionType).types;
17864- return getUnionType(types.map(type => {
17865- return resolveCustomJsxElementAttributesType(openingLikeElement, type);
17866- }), UnionReduction.Subtype);
17867- }
17868-
17869- // Shortcircuit any
17870- if (isTypeAny(elementType)) {
17871- return elementType;
17872- }
17873- // If the elemType is a string type, we have to return anyType to prevent an error downstream as we will try to find construct or call signature of the type
17874- else if (elementType.flags & TypeFlags.String) {
17875- return anyType;
17876- }
17877- else if (elementType.flags & TypeFlags.StringLiteral) {
17878- return getIntrinsicAttributesTypeFromStringLiteralType(elementType as StringLiteralType, openingLikeElement) || anyType;
17879- }
17880-
17881- // Get the element instance type (the result of newing or invoking this tag)
17882- const preferedOverload = getNodeLinks(openingLikeElement).resolvedSignature === resolvingSignature ? resolvingSignature : getResolvedSignature(openingLikeElement);
17883- if (!preferedOverload) {
17884- return errorType;
17885- }
17886- const isSFC = !length(getSignaturesOfType(elementType, SignatureKind.Construct));
17887- return isSFC ? getJsxPropsTypeFromCallSignature(preferedOverload, openingLikeElement) : getJsxPropsTypeFromClassType(preferedOverload, isInJSFile(openingLikeElement), openingLikeElement);
17888- }
17889-
1789017872 function checkJsxReturnAssignableToAppropriateBound(isSFC: boolean, elemInstanceType: Type, openingLikeElement: Node) {
1789117873 if (isSFC) {
1789217874 const sfcReturnConstraint = getJsxStatelessElementTypeAt(openingLikeElement);
@@ -17926,29 +17908,6 @@ namespace ts {
1792617908 return links.resolvedJsxElementAttributesType;
1792717909 }
1792817910
17929- /**
17930- * Get attributes type of the given custom opening-like JSX element.
17931- * This function is intended to be called from a caller that handles intrinsic JSX element already.
17932- * @param node a custom JSX opening-like element
17933- */
17934- function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement): Type {
17935- return resolveCustomJsxElementAttributesType(node, checkExpression(node.tagName));
17936- }
17937-
17938- /**
17939- * Get the attributes type, which indicates the attributes that are valid on the given JSXOpeningLikeElement.
17940- * @param node a JSXOpeningLikeElement node
17941- * @return an attributes type of the given node
17942- */
17943- function getAttributesTypeFromJsxOpeningLikeElement(node: JsxOpeningLikeElement): Type {
17944- if (isJsxIntrinsicIdentifier(node.tagName)) {
17945- return getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
17946- }
17947- else {
17948- return getCustomJsxElementAttributesType(node);
17949- }
17950- }
17951-
1795217911 function getJsxElementClassTypeAt(location: Node): Type | undefined {
1795317912 const type = getJsxType(JsxNames.ElementClass, location);
1795417913 if (type === errorType) return undefined;
@@ -18900,8 +18859,7 @@ namespace ts {
1890018859 }
1890118860
1890218861 function inferJsxTypeArguments(node: JsxOpeningLikeElement, signature: Signature, excludeArgument: ReadonlyArray<boolean> | undefined, context: InferenceContext): Type[] {
18903- const isCtor = !!length(getSignaturesOfType(checkExpression(node.tagName), SignatureKind.Construct));
18904- const paramType = isCtor ? getJsxPropsTypeFromClassType(signature, isInJSFile(node), node) : getJsxPropsTypeFromCallSignature(signature, node);
18862+ const paramType = getEffectiveFirstArgumentForJsxSignature(signature, node);
1890518863
1890618864 const checkAttrType = checkExpressionWithContextualType(node.attributes, paramType, excludeArgument && excludeArgument[0] !== undefined ? identityMapper : context);
1890718865 inferTypes(context.inferences, checkAttrType, paramType);
@@ -19046,7 +19004,7 @@ namespace ts {
1904619004
1904719005 function isJsxStatelessFunctionReference(node: JsxOpeningLikeElement) {
1904819006 if (isJsxIntrinsicIdentifier(node.tagName)) {
19049- return false ;
19007+ return true ;
1905019008 }
1905119009 const tagType = checkExpression(node.tagName);
1905219010 return !length(getSignaturesOfType(getApparentType(tagType), SignatureKind.Construct));
@@ -19059,11 +19017,11 @@ namespace ts {
1905919017 * @param relation a relationship to check parameter and argument type
1906019018 * @param excludeArgument
1906119019 */
19062- function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, isCtor: boolean, relation: Map<RelationComparisonResult>, reportErrors: boolean) {
19020+ function checkApplicableSignatureForJsxOpeningLikeElement(node: JsxOpeningLikeElement, signature: Signature, relation: Map<RelationComparisonResult>, reportErrors: boolean) {
1906319021 // Stateless function components can have maximum of three arguments: "props", "context", and "updater".
1906419022 // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props,
1906519023 // can be specified by users through attributes property.
19066- const paramType = isCtor ? getJsxPropsTypeFromClassType(signature, isInJSFile(node), node) : getJsxPropsTypeFromCallSignature (signature, node);
19024+ const paramType = getEffectiveFirstArgumentForJsxSignature (signature, node);
1906719025 const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined);
1906819026 return checkTypeRelatedToAndOptionallyElaborate(attributesType, paramType, relation, reportErrors ? node.tagName : undefined, node.attributes);
1906919027 }
@@ -19076,7 +19034,7 @@ namespace ts {
1907619034 excludeArgument: boolean[] | undefined,
1907719035 reportErrors: boolean) {
1907819036 if (isJsxOpeningLikeElement(node)) {
19079- return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, !isJsxStatelessFunctionReference(node), relation, reportErrors);
19037+ return checkApplicableSignatureForJsxOpeningLikeElement(node, signature, relation, reportErrors);
1908019038 }
1908119039 const thisType = getThisTypeOfSignature(signature);
1908219040 if (thisType && thisType !== voidType && node.kind !== SyntaxKind.NewExpression) {
@@ -20000,33 +19958,63 @@ namespace ts {
2000019958 return resolveCall(node, callSignatures, candidatesOutArray, isForSignatureHelp, headMessage);
2000119959 }
2000219960
19961+ function createSignatureForJSXIntrinsic(node: JsxOpeningLikeElement, result: Type): Signature {
19962+ // TODO: Since this builds some nodes for the fake'd up signature, this might be worth caching on the node
19963+ const namespace = getJsxNamespaceAt(node);
19964+ const exports = namespace && getExportsOfSymbol(namespace);
19965+ // TODO: We fake up a SFC signature for each intrinsic, however a more specific per-element signature drawn from the JSX declaration
19966+ // file would probably be preferable.
19967+ const typeSymbol = exports && getSymbol(exports, JsxNames.Element, SymbolFlags.Type);
19968+ const returnNode = typeSymbol && nodeBuilder.symbolToEntityName(typeSymbol, SymbolFlags.Type, node);
19969+ const declaration = createFunctionTypeNode(/*typeParameters*/ undefined,
19970+ [createParameter(/*decorators*/ undefined, /*modifiers*/ undefined, /*dotdotdot*/ undefined, "props", /*questionMark*/ undefined, nodeBuilder.typeToTypeNode(result, node))],
19971+ returnNode ? createTypeReferenceNode(returnNode, /*typeArguments*/ undefined) : createKeywordTypeNode(SyntaxKind.AnyKeyword)
19972+ );
19973+ const parameterSymbol = createSymbol(SymbolFlags.FunctionScopedVariable, "props" as __String);
19974+ parameterSymbol.type = result;
19975+ return createSignature(
19976+ declaration,
19977+ /*typeParameters*/ undefined,
19978+ /*thisParameter*/ undefined,
19979+ [parameterSymbol],
19980+ typeSymbol ? getDeclaredTypeOfSymbol(typeSymbol) : errorType,
19981+ /*returnTypePredicate*/ undefined,
19982+ 1,
19983+ /*hasRestparameter*/ false,
19984+ /*hasLiteralTypes*/ false
19985+ );
19986+ }
19987+
2000319988 function resolveJsxOpeningLikeElement(node: JsxOpeningLikeElement, candidatesOutArray: Signature[] | undefined, isForSignatureHelp: boolean): Signature {
2000419989 if (isJsxIntrinsicIdentifier(node.tagName)) {
2000519990 const result = getIntrinsicAttributesTypeFromJsxOpeningLikeElement(node);
20006- checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(node.attributes, CheckMode.Normal), result, node.tagName, node.attributes);
20007- return anySignature; // TODO: Fake up a signature from the intrinsic types
19991+ const fakeSignature = createSignatureForJSXIntrinsic(node, result);
19992+ checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined), result, node.tagName, node.attributes);
19993+ return fakeSignature;
2000819994 }
2000919995 const exprTypes = checkExpression(node.tagName);
2001019996 const apparentType = getApparentType(exprTypes);
2001119997 if (apparentType === errorType) {
2001219998 return resolveErrorCall(node);
2001319999 }
2001420000
20015- const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
20016- const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
20017- if (isUntypedFunctionCall(exprTypes, apparentType, callSignatures.length, constructSignatures.length) || exprTypes.flags & TypeFlags.String) {
20018- return resolveUntypedCall(node);
20019- }
20020-
2002120001 if (exprTypes.flags & TypeFlags.StringLiteral) {
2002220002 const intrinsicType = getIntrinsicAttributesTypeFromStringLiteralType(exprTypes as StringLiteralType, node);
2002320003 if (!intrinsicType) {
2002420004 error(node, Diagnostics.Property_0_does_not_exist_on_type_1, (exprTypes as StringLiteralType).value, "JSX." + JsxNames.IntrinsicElements);
20005+ return resolveUntypedCall(node);
2002520006 }
2002620007 else {
20027- checkTypeAssignableToAndOptionallyElaborate(checkExpressionCached(node.attributes, CheckMode.Normal), intrinsicType, node.tagName, node.attributes);
20008+ const fakeSignature = createSignatureForJSXIntrinsic(node, intrinsicType);
20009+ checkTypeAssignableToAndOptionallyElaborate(checkExpressionWithContextualType(node.attributes, getEffectiveFirstArgumentForJsxSignature(fakeSignature, node), /*mapper*/ undefined), intrinsicType, node.tagName, node.attributes);
20010+ return fakeSignature;
2002820011 }
20029- return anySignature; // TODO: Fake up a signature from the intrinsic types
20012+ }
20013+
20014+ const callSignatures = getSignaturesOfType(apparentType, SignatureKind.Call);
20015+ const constructSignatures = getSignaturesOfType(apparentType, SignatureKind.Construct);
20016+ if (exprTypes.flags & TypeFlags.String || isUntypedFunctionCall(exprTypes, apparentType, callSignatures.length, constructSignatures.length)) {
20017+ return resolveUntypedCall(node);
2003020018 }
2003120019
2003220020 const signatures = getUninstantiatedJsxSignaturesOfType(apparentType);
0 commit comments