@@ -411,6 +411,9 @@ namespace ts {
411
411
412
412
let _jsxNamespace: string;
413
413
let _jsxFactoryEntity: EntityName;
414
+ let _jsxElementAttribPropInterfaceSymbol: Symbol; // JSX.ElementAttributesProperty [symbol]
415
+ let _jsxElementPropertiesName: string;
416
+ let _jsxElementChildrenPropertyName: string;
414
417
415
418
/** Things we lazy load from the JSX namespace */
416
419
const jsxTypes = createMap<Type>();
@@ -424,8 +427,6 @@ namespace ts {
424
427
IntrinsicClassAttributes: "IntrinsicClassAttributes"
425
428
};
426
429
427
- const jsxChildrenPropertyName = "children";
428
-
429
430
const subtypeRelation = createMap<RelationComparisonResult>();
430
431
const assignableRelation = createMap<RelationComparisonResult>();
431
432
const comparableRelation = createMap<RelationComparisonResult>();
@@ -458,7 +459,7 @@ namespace ts {
458
459
return checker;
459
460
460
461
function getJsxNamespace(): string {
461
- if (_jsxNamespace === undefined ) {
462
+ if (! _jsxNamespace) {
462
463
_jsxNamespace = "React";
463
464
if (compilerOptions.jsxFactory) {
464
465
_jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
@@ -12652,8 +12653,9 @@ namespace ts {
12652
12653
return getTypeOfPropertyOfType(attributesType, (node.parent as JsxAttribute).name.text);
12653
12654
}
12654
12655
else if (node.parent.kind === SyntaxKind.JsxElement) {
12655
- // JSX expression is in children of JSX Element, we will look for an atttribute named "chidlren"
12656
- return getTypeOfPropertyOfType(attributesType, jsxChildrenPropertyName);
12656
+ // JSX expression is in children of JSX Element, we will look for an "children" atttribute (we get the name from JSX.ElementAttributesProperty)
12657
+ const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
12658
+ return jsxChildrenPropertyName ? getTypeOfPropertyOfType(attributesType, jsxChildrenPropertyName) : anyType;
12657
12659
}
12658
12660
else {
12659
12661
// JSX expression is in JSX spread attribute
@@ -13298,18 +13300,9 @@ namespace ts {
13298
13300
}
13299
13301
13300
13302
// Handle children attribute
13301
- const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ?
13302
- openingLikeElement.parent as JsxElement : undefined;
13303
+ const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ? openingLikeElement.parent as JsxElement : undefined;
13303
13304
// We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement
13304
13305
if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) {
13305
- // Error if there is a attribute named "children" and children element.
13306
- // This is because children element will overwrite the value from attributes
13307
- if (attributesTable.has(jsxChildrenPropertyName)) {
13308
- error(attributes, Diagnostics.props_children_are_specified_twice_The_attribute_named_children_will_be_overwritten);
13309
- }
13310
-
13311
- // If there are children in the body of JSX element, create dummy attribute "children" with anyType so that it will pass the attribute checking process
13312
- const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName);
13313
13306
const childrenTypes: Type[] = [];
13314
13307
for (const child of (parent as JsxElement).children) {
13315
13308
// In React, JSX text that contains only whitespaces will be ignored so we don't want to type-check that
@@ -13323,11 +13316,22 @@ namespace ts {
13323
13316
childrenTypes.push(checkExpression(child, checkMode));
13324
13317
}
13325
13318
}
13326
- childrenPropSymbol.type = childrenTypes.length === 1 ?
13327
- childrenTypes[0] :
13328
- createArrayType(getUnionType(childrenTypes, /*subtypeReduction*/ false));
13329
- attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol);
13330
- containsSynthesizedJsxChildren = true;
13319
+
13320
+ // Error if there is a attribute named "children" and children element.
13321
+ // This is because children element will overwrite the value from attributes
13322
+ const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
13323
+ if (jsxChildrenPropertyName) {
13324
+ if (attributesTable.has(jsxChildrenPropertyName)) {
13325
+ error(attributes, Diagnostics.props_children_are_specified_twice_The_attribute_named_children_will_be_overwritten);
13326
+ }
13327
+
13328
+ // If there are children in the body of JSX element, create dummy attribute "children" with anyType so that it will pass the attribute checking process
13329
+ const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName);
13330
+ childrenPropSymbol.type = childrenTypes.length === 1 ?
13331
+ childrenTypes[0] :
13332
+ createArrayType(getUnionType(childrenTypes, /*subtypeReduction*/ false));
13333
+ attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol);
13334
+ }
13331
13335
}
13332
13336
13333
13337
return createJsxAttributesType(attributes.symbol, attributesTable);
@@ -13429,41 +13433,85 @@ namespace ts {
13429
13433
return getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
13430
13434
}
13431
13435
13436
+ function getPropertiesFromJsxElementAttributesProperty() {
13437
+ if (!_jsxElementAttribPropInterfaceSymbol) {
13438
+ // JSX
13439
+ const jsxNamespace = getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/undefined);
13440
+ // JSX.ElementAttributesProperty [symbol]
13441
+ _jsxElementAttribPropInterfaceSymbol = jsxNamespace && getSymbol(jsxNamespace.exports, JsxNames.ElementAttributesPropertyNameContainer, SymbolFlags.Type);
13442
+ }
13443
+ return _jsxElementAttribPropInterfaceSymbol;
13444
+ }
13445
+
13432
13446
/// e.g. "props" for React.d.ts,
13433
13447
/// or 'undefined' if ElementAttributesProperty doesn't exist (which means all
13434
13448
/// non-intrinsic elements' attributes type is 'any'),
13435
13449
/// or '' if it has 0 properties (which means every
13436
13450
/// non-intrinsic elements' attributes type is the element instance type)
13437
13451
function getJsxElementPropertiesName() {
13438
- // JSX
13439
- const jsxNamespace = getGlobalSymbol(JsxNames.JSX, SymbolFlags.Namespace, /*diagnosticMessage*/undefined);
13440
- // JSX.ElementAttributesProperty [symbol]
13441
- const attribsPropTypeSym = jsxNamespace && getSymbol(jsxNamespace.exports, JsxNames.ElementAttributesPropertyNameContainer, SymbolFlags.Type);
13442
- // JSX.ElementAttributesProperty [type]
13443
- const attribPropType = attribsPropTypeSym && getDeclaredTypeOfSymbol(attribsPropTypeSym);
13444
- // The properties of JSX.ElementAttributesProperty
13445
- const attribProperties = attribPropType && getPropertiesOfType(attribPropType);
13446
-
13447
- if (attribProperties) {
13448
- // Element Attributes has zero properties, so the element attributes type will be the class instance type
13449
- if (attribProperties.length === 0) {
13450
- return "";
13451
- }
13452
- // Element Attributes has one property, so the element attributes type will be the type of the corresponding
13453
- // property of the class instance type
13454
- else if (attribProperties.length === 1) {
13455
- return attribProperties[0].name;
13456
- }
13457
- // More than one property on ElementAttributesProperty is an error
13452
+ if (!_jsxElementPropertiesName) {
13453
+ const jsxElementAttribPropInterfaceSym = getPropertiesFromJsxElementAttributesProperty();
13454
+ // JSX.ElementAttributesProperty [type]
13455
+ const jsxElementAttribPropInterfaceType = jsxElementAttribPropInterfaceSym && getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym);
13456
+ // The properties of JSX.ElementAttributesProperty
13457
+ const propertiesOfJsxElementAttribPropInterface = jsxElementAttribPropInterfaceType && getPropertiesOfType(jsxElementAttribPropInterfaceType);
13458
+
13459
+ // if there is a property in JSX.ElementAttributesProperty
13460
+ // i.e.
13461
+ // interface ElementAttributesProperty {
13462
+ // props: {
13463
+ // children?: any;
13464
+ // };
13465
+ // }
13466
+ if (propertiesOfJsxElementAttribPropInterface) {
13467
+ // Element Attributes has zero properties, so the element attributes type will be the class instance type
13468
+ if (propertiesOfJsxElementAttribPropInterface.length === 0) {
13469
+ _jsxElementPropertiesName = "";
13470
+ }
13471
+ // Element Attributes has one property, so the element attributes type will be the type of the corresponding
13472
+ // property of the class instance type
13473
+ else if (propertiesOfJsxElementAttribPropInterface.length === 1) {
13474
+ _jsxElementPropertiesName = propertiesOfJsxElementAttribPropInterface[0].name;
13475
+ }
13476
+ // More than one property on ElementAttributesProperty is an error
13477
+ else {
13478
+ error(jsxElementAttribPropInterfaceSym.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, JsxNames.ElementAttributesPropertyNameContainer);
13479
+ _jsxElementPropertiesName = undefined;
13480
+ }
13481
+ }
13458
13482
else {
13459
- error(attribsPropTypeSym.declarations[0], Diagnostics.The_global_type_JSX_0_may_not_have_more_than_one_property, JsxNames.ElementAttributesPropertyNameContainer);
13460
- return undefined;
13483
+ // No interface exists, so the element attributes type will be an implicit any
13484
+ _jsxElementPropertiesName = undefined;
13461
13485
}
13462
13486
}
13463
- else {
13464
- // No interface exists, so the element attributes type will be an implicit any
13465
- return undefined;
13487
+
13488
+ return _jsxElementPropertiesName;
13489
+ }
13490
+
13491
+ function getJsxElementChildrenPropertyname(): string {
13492
+ if (!_jsxElementChildrenPropertyName) {
13493
+ const jsxElementAttribPropInterfaceSym = getPropertiesFromJsxElementAttributesProperty();
13494
+ // JSX.ElementAttributesProperty [type]
13495
+ const jsxElementAttribPropInterfaceType = jsxElementAttribPropInterfaceSym && getDeclaredTypeOfSymbol(jsxElementAttribPropInterfaceSym);
13496
+ // The properties of JSX.ElementAttributesProperty
13497
+ const propertiesOfJsxElementAttribPropInterface = jsxElementAttribPropInterfaceType && getPropertiesOfType(jsxElementAttribPropInterfaceType);
13498
+ // if there is a property in JSX.ElementAttributesProperty
13499
+ // i.e.
13500
+ // interface ElementAttributesProperty {
13501
+ // props: {
13502
+ // children?: any;
13503
+ // };
13504
+ // }
13505
+ if (propertiesOfJsxElementAttribPropInterface && propertiesOfJsxElementAttribPropInterface.length === 1) {
13506
+ const propsType = getTypeOfSymbol(propertiesOfJsxElementAttribPropInterface[0]);
13507
+ const propertiesOfProps = propsType && getPropertiesOfType(propsType);
13508
+ if (propertiesOfProps && propertiesOfProps.length === 1) {
13509
+ _jsxElementChildrenPropertyName = propertiesOfProps[0].name;
13510
+ }
13511
+ }
13466
13512
}
13513
+
13514
+ return _jsxElementChildrenPropertyName;
13467
13515
}
13468
13516
13469
13517
/**
0 commit comments