Skip to content

Commit aea551c

Browse files
author
Kanchalai Tanglertsampan
committed
Address code review
1 parent 42c0816 commit aea551c

File tree

3 files changed

+57
-65
lines changed

3 files changed

+57
-65
lines changed

src/compiler/checker.ts

Lines changed: 50 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -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) {

src/services/goToDefinition.ts

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -86,15 +86,13 @@ namespace ts.GoToDefinition {
8686
}
8787

8888
if (isJsxOpeningLikeElement(node.parent)) {
89-
// For JSX opening-like element, the tag can be resolved either as stateful component (e.g class) or stateless function component.
90-
// Because if it is a stateless function component with an error while trying to resolve the signature, we don't want to return all
91-
// possible overloads but just the first one.
89+
// If there are errors when trying to figure out stateless component function, just return the first declaration
9290
// For example:
93-
// /*firstSource*/declare function MainButton(buttonProps: ButtonProps): JSX.Element;
94-
// /*secondSource*/declare function MainButton(linkProps: LinkProps): JSX.Element;
95-
// /*thirdSource*/declare function MainButton(props: ButtonProps | LinkProps): JSX.Element;
96-
// let opt = <Main/*firstTarget*/Button />; // We get undefined for resolved signature indicating an error, then just return the first declaration
97-
const {symbolName, symbolKind, containerName} = getSymbolInfo(typeChecker, symbol, node);
91+
// declare function /*firstSource*/iMainButton(buttonProps: ButtonProps): JSX.Element;
92+
// declare function /*secondSource*/MainButton(linkProps: LinkProps): JSX.Element;
93+
// declare function /*thirdSource*/MainButton(props: ButtonProps | LinkProps): JSX.Element;
94+
// let opt = <Main/*firstTarget*/Button />; // Error - We get undefined for resolved signature indicating an error, then just return the first declaration
95+
const {symbolName, symbolKind, containerName} = getSymbolInfo(typeChecker, symbol, node);
9896
return [createDefinitionInfo(symbol.valueDeclaration, symbolKind, symbolName, containerName)];
9997
}
10098

src/services/symbolDisplay.ts

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,7 @@ namespace ts.SymbolDisplay {
136136
signature = candidateSignatures[0];
137137
}
138138

139-
let useConstructSignatures = false;
140-
if (callExpressionLike.kind === SyntaxKind.NewExpression) {
141-
useConstructSignatures = true;
142-
}
143-
else if (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword) {
144-
useConstructSignatures = true;
145-
}
139+
const useConstructSignatures = callExpressionLike.kind === SyntaxKind.NewExpression || (isCallExpression(callExpressionLike) && callExpressionLike.expression.kind === SyntaxKind.SuperKeyword);
146140

147141
const allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures();
148142

0 commit comments

Comments
 (0)