@@ -11398,7 +11398,7 @@ namespace ts {
1139811398 // When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give its attributes a contextual type
1139911399 // which is a type of the parameter of the signature we are trying out.
1140011400 // If there is no contextual type (e.g. we are trying to resolve stateful component), get attributes type from resolving element's tagName
11401- const attributesType = getContextualType(<Expression>attribute.parent) || getAttributesTypeFromJsxOpeningLikeElement(<JsxOpeningLikeElement>attribute.parent.parent) ;
11401+ const attributesType = getContextualType(<Expression>attribute.parent);
1140211402
1140311403 if (isJsxAttribute(attribute)) {
1140411404 if (!attributesType || isTypeAny(attributesType)) {
@@ -12071,42 +12071,6 @@ namespace ts {
1207112071 return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, /*filter*/ undefined, contextualMapper);
1207212072 }
1207312073
12074- /**
12075- * Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes.
12076- * Get the attributes type of the opening-like element through resolving the tagName, "target attributes"
12077- * Check assignablity between given attributes property, "source attributes", and the "target attributes"
12078- * @param openingLikeElement an opening-like JSX element to check its JSXAttributes
12079- */
12080- function checkJsxAttributesAssignableToTagNameAttributes(openingLikeElement: JsxOpeningLikeElement) {
12081- // The function involves following steps:
12082- // 1. Figure out expected attributes type by resolving tagName of the JSX opening-like element, targetAttributesType.
12083- // During these steps, we will try to resolve the tagName as intrinsic name, stateless function, stateful component (in the order)
12084- // 2. Solved JSX attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element.
12085- // 3. Check if the two are assignable to each other
12086-
12087- // targetAttributesType is a type of an attributes from resolving tagName of an opening-like JSX element.
12088- const targetAttributesType = isJsxIntrinsicIdentifier(openingLikeElement.tagName) ?
12089- getIntrinsicAttributesTypeFromJsxOpeningLikeElement(openingLikeElement) :
12090- getCustomJsxElementAttributesType(openingLikeElement, /*shouldIncludeAllStatelessAttributesType*/ false);
12091-
12092- // sourceAttributesType is a type of an attributes properties.
12093- // i.e <div attr1={10} attr2="string" />
12094- // attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes".
12095- const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement,
12096- attribute => {
12097- return isUnhyphenatedJsxName(attribute.name) || !!(getPropertyOfType(targetAttributesType, attribute.name));
12098- });
12099-
12100- // If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type.
12101- // but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass.
12102- if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || (<ResolvedType>sourceAttributesType).properties.length > 0)) {
12103- error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
12104- }
12105- else {
12106- checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
12107- }
12108- }
12109-
1211012074 function getJsxType(name: string) {
1211112075 let jsxType = jsxTypes.get(name);
1211212076 if (jsxType === undefined) {
@@ -12462,6 +12426,7 @@ namespace ts {
1246212426 * Get attributes type of the given custom opening-like JSX element.
1246312427 * This function is intended to be called from a caller that handles intrinsic JSX element already.
1246412428 * @param node a custom JSX opening-like element
12429+ * @param shouldIncludeAllStatelessAttributesType a boolean value used by language service to get all possible attributes type from an overload stateless function component
1246512430 */
1246612431 function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type {
1246712432 const links = getNodeLinks(node);
@@ -12489,7 +12454,7 @@ namespace ts {
1248912454 }
1249012455
1249112456 /**
12492- * Get the attributes type which is the type that indicate which attributes are valid on the given JSXOpeningLikeElement.
12457+ * Get the attributes type, which indicates the attributes that are valid on the given JSXOpeningLikeElement.
1249312458 * @param node a JSXOpeningLikeElement node
1249412459 * @return an attributes type of the given node
1249512460 */
@@ -12520,7 +12485,9 @@ namespace ts {
1252012485 return jsxElementClassType;
1252112486 }
1252212487
12523- // Returns all the properties of the Jsx.IntrinsicElements interface
12488+ /**
12489+ * Returns all the properties of the Jsx.IntrinsicElements interface
12490+ */
1252412491 function getJsxIntrinsicTagNames(): Symbol[] {
1252512492 const intrinsics = getJsxType(JsxNames.IntrinsicElements);
1252612493 return intrinsics ? getPropertiesOfType(intrinsics) : emptyArray;
@@ -12561,6 +12528,42 @@ namespace ts {
1256112528 checkJsxAttributesAssignableToTagNameAttributes(node);
1256212529 }
1256312530
12531+ /**
12532+ * Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes.
12533+ * Get the attributes type of the opening-like element through resolving the tagName, "target attributes"
12534+ * Check assignablity between given attributes property, "source attributes", and the "target attributes"
12535+ * @param openingLikeElement an opening-like JSX element to check its JSXAttributes
12536+ */
12537+ function checkJsxAttributesAssignableToTagNameAttributes(openingLikeElement: JsxOpeningLikeElement) {
12538+ // The function involves following steps:
12539+ // 1. Figure out expected attributes type by resolving tagName of the JSX opening-like element, targetAttributesType.
12540+ // During these steps, we will try to resolve the tagName as intrinsic name, stateless function, stateful component (in the order)
12541+ // 2. Solved JSX attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element.
12542+ // 3. Check if the two are assignable to each other
12543+
12544+ // targetAttributesType is a type of an attributes from resolving tagName of an opening-like JSX element.
12545+ const targetAttributesType = isJsxIntrinsicIdentifier(openingLikeElement.tagName) ?
12546+ getIntrinsicAttributesTypeFromJsxOpeningLikeElement(openingLikeElement) :
12547+ getCustomJsxElementAttributesType(openingLikeElement, /*shouldIncludeAllStatelessAttributesType*/ false);
12548+
12549+ // sourceAttributesType is a type of an attributes properties.
12550+ // i.e <div attr1={10} attr2="string" />
12551+ // attr1 and attr2 are treated as JSXAttributes attached in the JsxOpeningLikeElement as "attributes".
12552+ const sourceAttributesType = createJsxAttributesTypeFromAttributesProperty(openingLikeElement,
12553+ attribute => {
12554+ return isUnhyphenatedJsxName(attribute.name) || !!(getPropertyOfType(targetAttributesType, attribute.name));
12555+ });
12556+
12557+ // If the targetAttributesType is an emptyObjectType, indicating that there is no property named 'props' on this instance type.
12558+ // but there exists a sourceAttributesType, we need to explicitly give an error as normal assignability check allow excess properties and will pass.
12559+ if (targetAttributesType === emptyObjectType && (isTypeAny(sourceAttributesType) || (<ResolvedType>sourceAttributesType).properties.length > 0)) {
12560+ error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
12561+ }
12562+ else {
12563+ checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
12564+ }
12565+ }
12566+
1256412567 function checkJsxExpression(node: JsxExpression, contextualMapper?: TypeMapper) {
1256512568 if (node.expression) {
1256612569 const type = checkExpression(node.expression, contextualMapper);
@@ -13021,9 +13024,7 @@ namespace ts {
1302113024 let spreadArgIndex = -1;
1302213025
1302313026 if (isJsxOpeningLikeElement(node)) {
13024- // For JSX opening-like element, we will ignore regular arity check (which is what is done here).
13025- // Instead, the arity check will be done in "checkApplicableSignatureForJsxOpeningLikeElement" as we are required to figure out
13026- // all property inside the given attributes.
13027+ // The arity check will be done in "checkApplicableSignatureForJsxOpeningLikeElement".
1302713028 return true;
1302813029 }
1302913030
@@ -13250,14 +13251,14 @@ namespace ts {
1325013251 // However "context" and "updater" are implicit and can't be specify by users. Only the first parameter, props,
1325113252 // can be specified by users through attributes property.
1325213253 const paramType = getTypeAtPosition(signature, 0);
13253- const argType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined);
13254- const argProperties = getPropertiesOfType(argType );
13254+ const attributesType = checkExpressionWithContextualType(node.attributes, paramType, /*contextualMapper*/ undefined);
13255+ const argProperties = getPropertiesOfType(attributesType );
1325513256 for (const arg of argProperties) {
1325613257 if (!getPropertyOfType(paramType, arg.name) && isUnhyphenatedJsxName(arg.name)) {
1325713258 return false;
1325813259 }
1325913260 }
13260- if (checkTypeRelatedTo(argType , paramType, relation, /*errorNode*/ undefined, headMessage)) {
13261+ if (checkTypeRelatedTo(attributesType , paramType, relation, /*errorNode*/ undefined, headMessage)) {
1326113262 return true;
1326213263 }
1326313264 return false;
@@ -13744,16 +13745,15 @@ namespace ts {
1374413745 // If candidate is undefined, it means that no candidates had a suitable arity. In that case,
1374513746 // skip the checkApplicableSignature check.
1374613747 if (candidateForArgumentError) {
13748+ if (isJsxOpeningOrSelfClosingElement) {
13749+ // We do not report any error here because any error will be handled in "resolveCustomJsxElementAttributesType".
13750+ return candidateForArgumentError;
13751+ }
1374713752 // excludeArgument is undefined, in this case also equivalent to [undefined, undefined, ...]
1374813753 // The importance of excludeArgument is to prevent us from typing function expression parameters
1374913754 // in arguments too early. If possible, we'd like to only type them once we know the correct
1375013755 // overload. However, this matters for the case where the call is correct. When the call is
1375113756 // an error, we don't need to exclude any arguments, although it would cause no harm to do so.
13752- if (isJsxOpeningOrSelfClosingElement) {
13753- // If there is not result, just return the last one we try as a candidate.
13754- // We do not report any error here because any error will be handled in "resolveCustomJsxElementAttributesType".
13755- return candidateForArgumentError;
13756- }
1375713757 checkApplicableSignature(node, args, candidateForArgumentError, assignableRelation, /*excludeArgument*/ undefined, /*reportErrors*/ true);
1375813758 }
1375913759 else if (candidateForTypeArgumentError) {
0 commit comments