Skip to content

Commit 0b4f25c

Browse files
author
Kanchalai Tanglertsampan
committed
Handle inference of function expression/arrow functions attribute
1 parent ab2e14f commit 0b4f25c

File tree

1 file changed

+32
-19
lines changed

1 file changed

+32
-19
lines changed

src/compiler/checker.ts

Lines changed: 32 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -6877,7 +6877,7 @@ namespace ts {
68776877

68786878
// Returns true if the given expression contains (at any level of nesting) a function or arrow expression
68796879
// that is subject to contextual typing.
6880-
function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike): boolean {
6880+
function isContextSensitive(node: Expression | MethodDeclaration | ObjectLiteralElementLike | JsxAttributeLike): boolean {
68816881
Debug.assert(node.kind !== SyntaxKind.MethodDeclaration || isObjectLiteralMethod(node));
68826882
switch (node.kind) {
68836883
case SyntaxKind.FunctionExpression:
@@ -6900,6 +6900,14 @@ namespace ts {
69006900
return isContextSensitiveFunctionLikeDeclaration(<MethodDeclaration>node);
69016901
case SyntaxKind.ParenthesizedExpression:
69026902
return isContextSensitive((<ParenthesizedExpression>node).expression);
6903+
case SyntaxKind.JsxAttributes:
6904+
return forEach((<JsxAttributes>node).properties, isContextSensitive);
6905+
case SyntaxKind.JsxAttribute:
6906+
// If written as a shorthand (e.g <... attr /> then there is no explicit initializer as it has implicit boolean value of true
6907+
// which is not context sensitvie.
6908+
return (<JsxAttribute>node).initializer && isContextSensitive((<JsxAttribute>node).initializer);
6909+
case SyntaxKind.JsxExpression:
6910+
return isContextSensitive((<JsxExpression>node).expression);
69036911
}
69046912

69056913
return false;
@@ -11281,7 +11289,7 @@ namespace ts {
1128111289

1128211290
function getContextualTypeForJsxAttribute(attribute: JsxAttribute | JsxSpreadAttribute) {
1128311291
// When we trying to resolve JsxOpeningLikeElement as a stateless function element, we will already give JSXAttributes a contextual type
11284-
// which is a type of the parameter of the signature we are trying out. This is not the case if it is a stateful JSX (i.e ReactComponenet class)
11292+
// which is a type of the parameter of the signature we are trying out. This is not the case if it is a stateful JSX (i.e ReactComponenet class)
1128511293
// So if that is the case, just return the type of the JsxAttribute in such contextual type with out going into resolving of the JsxOpeningLikeElement again
1128611294
const attributesType = getContextualType(<Expression>attribute.parent) || getAttributesTypeFromJsxOpeningLikeElement(<JsxOpeningLikeElement>attribute.parent.parent);
1128711295

@@ -11870,7 +11878,7 @@ namespace ts {
1187011878
* @param openingLikeElement a Jsx opening-like element
1187111879
* @return an anonymous type (similar to the one returned by checkObjectLiteral) in which its properties are attributes property.
1187211880
*/
11873-
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, filter?: (symbol: Symbol) => boolean) {
11881+
function createJsxAttributesTypeFromAttributesProperty(openingLikeElement: JsxOpeningLikeElement, filter?: (symbol: Symbol) => boolean, contextualMapper?: TypeMapper) {
1187411882
const attributes = openingLikeElement.attributes;
1187511883
let attributesTable = createMap<Symbol>();
1187611884
let spread: Type = emptyObjectType;
@@ -11879,7 +11887,7 @@ namespace ts {
1187911887
const member = attributeDecl.symbol;
1188011888
if (isJsxAttribute(attributeDecl)) {
1188111889
const exprType = attributeDecl.initializer ?
11882-
checkExpression(attributeDecl.initializer) :
11890+
checkExpression(attributeDecl.initializer, contextualMapper) :
1188311891
trueType; // <Elem attr /> is sugar for <Elem attr={true} />
1188411892

1188511893
const attributeSymbol = <TransientSymbol>createSymbol(SymbolFlags.Property | SymbolFlags.Transient | member.flags, member.name);
@@ -11951,12 +11959,12 @@ namespace ts {
1195111959
* the JSX opening-like element attributes property with contextual type.
1195211960
* @param node a JSXAttributes to be resolved of its type
1195311961
*/
11954-
function checkJsxAttributes(node: JsxAttributes) {
11955-
return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement);
11962+
function checkJsxAttributes(node: JsxAttributes, contextualMapper?: TypeMapper) {
11963+
return createJsxAttributesTypeFromAttributesProperty(node.parent as JsxOpeningLikeElement, /*filter*/ undefined, contextualMapper);
1195611964
}
1195711965

1195811966
/**
11959-
* Check whether the given attributes of JsxOpeningLikeElement is assignable to its corresponding tag-name attributes type.
11967+
* Check whether the given attributes of JsxOpeningLikeElement is assignable to the tag-name attributes type.
1196011968
* Resolve the type of attributes of the openingLikeElement through checking type of tag-name
1196111969
* Check assignablity between given attributes property, "attributes" and the target attributes resulted from resolving tag-name
1196211970
* @param openingLikeElement an opening-like JSX element to check its JSXAttributes
@@ -11965,7 +11973,7 @@ namespace ts {
1196511973
// The function involves following steps:
1196611974
// 1. Figure out expected attributes type expected by resolving tag-name of the JSX opening-like element, targetAttributesType.
1196711975
// During these steps, we will try to resolve the tag-name as intrinsic name, stateless function, stateful component (in the order)
11968-
// 2. Solved Jsx attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element.
11976+
// 2. Solved JSX attributes type given by users, sourceAttributesType, which is by resolving "attributes" property of the JSX opening-like element.
1196911977
// 3. Check if the two are assignable to each other
1197011978

1197111979
// targetAttributesType is a type of an attributes from resolving tag-name of an opening-like JSX element.
@@ -12190,12 +12198,17 @@ namespace ts {
1219012198
}
1219112199

1219212200
/**
12193-
* Resolve attributes type of the given node. The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element.
12201+
* Resolve attributes type of the given opening-like element. The attributes type is a type of attributes associated with the given elementType.
12202+
* For instance:
12203+
* declare function Foo(attr: { p1: string}): JSX.Element;
12204+
* <Foo p1={10} />; // This function will try resolve "Foo" and return an attributes type of "Foo" which is "{ p1: string }"
12205+
*
12206+
* The function is intended to initially be called from getAttributesTypeFromJsxOpeningLikeElement which already handle JSX-intrinsic-element.
1219412207
* @param openingLikeElement a non-intrinsic JSXOPeningLikeElement
1219512208
* @param shouldIncludeAllStatelessAttributesType a boolean indicating whether to include all attributes types from all stateless function signature
12196-
* @param elementType an instance type of the given node
12209+
* @param elementType an instance type of the given opening-like element
1219712210
* @param elementClassType a JSX-ElementClass type. This is a result of looking up ElementClass interface in the JSX global (imported from react.d.ts)
12198-
* @return attributes'type if able to resolve the type of node
12211+
* @return attributes type if able to resolve the type of node
1219912212
* anyType if there is no type ElementAttributesProperty or there is an error
1220012213
* emptyObjectType if there is no "prop" in the element instance type
1220112214
**/
@@ -12337,9 +12350,9 @@ namespace ts {
1233712350
}
1233812351

1233912352
/**
12340-
* Get attributes type of the given custom opening-like Jsx element.
12341-
* The function is intended to be called from a function which has handle intrinsic Jsx element already.
12342-
* @param node a custom Jsx opening-like element
12353+
* Get attributes type of the given custom opening-like JSX element.
12354+
* The function is intended to be called from a function which has handle intrinsic JSX element already.
12355+
* @param node a custom JSX opening-like element
1234312356
*/
1234412357
function getCustomJsxElementAttributesType(node: JsxOpeningLikeElement, shouldIncludeAllStatelessAttributesType: boolean): Type {
1234512358
const links = getNodeLinks(node);
@@ -12439,9 +12452,9 @@ namespace ts {
1243912452
checkJsxAttributesAssignableToTagnameAttributes(node);
1244012453
}
1244112454

12442-
function checkJsxExpression(node: JsxExpression) {
12455+
function checkJsxExpression(node: JsxExpression, contextualMapper?: TypeMapper) {
1244312456
if (node.expression) {
12444-
const type = checkExpression(node.expression);
12457+
const type = checkExpression(node.expression, contextualMapper);
1244512458
if (node.dotDotDotToken && type !== anyType && !isArrayType(type)) {
1244612459
error(node, Diagnostics.JSX_spread_child_must_be_an_array_type, node.toString(), typeToString(type));
1244712460
}
@@ -13554,7 +13567,7 @@ namespace ts {
1355413567
// For a decorator, no arguments are susceptible to contextual typing due to the fact
1355513568
// decorators are applied to a declaration by the emitter, and not to an expression.
1355613569
let excludeArgument: boolean[];
13557-
if (!isDecorator && !isJsxOpeningOrSelfClosingElement) {
13570+
if (!isDecorator) {
1355813571
// We do not need to call `getEffectiveArgumentCount` here as it only
1355913572
// applies when calculating the number of arguments for a decorator.
1356013573
for (let i = isTaggedTemplate ? 1 : 0; i < args.length; i++) {
@@ -15737,13 +15750,13 @@ namespace ts {
1573715750
case SyntaxKind.YieldExpression:
1573815751
return checkYieldExpression(<YieldExpression>node);
1573915752
case SyntaxKind.JsxExpression:
15740-
return checkJsxExpression(<JsxExpression>node);
15753+
return checkJsxExpression(<JsxExpression>node, contextualMapper);
1574115754
case SyntaxKind.JsxElement:
1574215755
return checkJsxElement(<JsxElement>node);
1574315756
case SyntaxKind.JsxSelfClosingElement:
1574415757
return checkJsxSelfClosingElement(<JsxSelfClosingElement>node);
1574515758
case SyntaxKind.JsxAttributes:
15746-
return checkJsxAttributes(<JsxAttributes>node);
15759+
return checkJsxAttributes(<JsxAttributes>node, contextualMapper);
1574715760
case SyntaxKind.JsxOpeningElement:
1574815761
Debug.fail("Shouldn't ever directly check a JsxOpeningElement");
1574915762
}

0 commit comments

Comments
 (0)