Skip to content

Commit c778a48

Browse files
author
Kanchalai Tanglertsampan
committed
Read "children" property from react DTS file
1 parent bb8bab3 commit c778a48

File tree

1 file changed

+93
-45
lines changed

1 file changed

+93
-45
lines changed

src/compiler/checker.ts

Lines changed: 93 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,9 @@ namespace ts {
411411

412412
let _jsxNamespace: string;
413413
let _jsxFactoryEntity: EntityName;
414+
let _jsxElementAttribPropInterfaceSymbol: Symbol; // JSX.ElementAttributesProperty [symbol]
415+
let _jsxElementPropertiesName: string;
416+
let _jsxElementChildrenPropertyName: string;
414417

415418
/** Things we lazy load from the JSX namespace */
416419
const jsxTypes = createMap<Type>();
@@ -424,8 +427,6 @@ namespace ts {
424427
IntrinsicClassAttributes: "IntrinsicClassAttributes"
425428
};
426429

427-
const jsxChildrenPropertyName = "children";
428-
429430
const subtypeRelation = createMap<RelationComparisonResult>();
430431
const assignableRelation = createMap<RelationComparisonResult>();
431432
const comparableRelation = createMap<RelationComparisonResult>();
@@ -458,7 +459,7 @@ namespace ts {
458459
return checker;
459460

460461
function getJsxNamespace(): string {
461-
if (_jsxNamespace === undefined) {
462+
if (!_jsxNamespace) {
462463
_jsxNamespace = "React";
463464
if (compilerOptions.jsxFactory) {
464465
_jsxFactoryEntity = parseIsolatedEntityName(compilerOptions.jsxFactory, languageVersion);
@@ -12652,8 +12653,9 @@ namespace ts {
1265212653
return getTypeOfPropertyOfType(attributesType, (node.parent as JsxAttribute).name.text);
1265312654
}
1265412655
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;
1265712659
}
1265812660
else {
1265912661
// JSX expression is in JSX spread attribute
@@ -13298,18 +13300,9 @@ namespace ts {
1329813300
}
1329913301

1330013302
// 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;
1330313304
// We have to check that openingElement of the parent is the one we are visiting as this may not be true for selfClosingElement
1330413305
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);
1331313306
const childrenTypes: Type[] = [];
1331413307
for (const child of (parent as JsxElement).children) {
1331513308
// 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 {
1332313316
childrenTypes.push(checkExpression(child, checkMode));
1332413317
}
1332513318
}
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+
}
1333113335
}
1333213336

1333313337
return createJsxAttributesType(attributes.symbol, attributesTable);
@@ -13429,41 +13433,85 @@ namespace ts {
1342913433
return getUnionType(map(signatures, getReturnTypeOfSignature), /*subtypeReduction*/ true);
1343013434
}
1343113435

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+
1343213446
/// e.g. "props" for React.d.ts,
1343313447
/// or 'undefined' if ElementAttributesProperty doesn't exist (which means all
1343413448
/// non-intrinsic elements' attributes type is 'any'),
1343513449
/// or '' if it has 0 properties (which means every
1343613450
/// non-intrinsic elements' attributes type is the element instance type)
1343713451
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+
}
1345813482
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;
1346113485
}
1346213486
}
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+
}
1346613512
}
13513+
13514+
return _jsxElementChildrenPropertyName;
1346713515
}
1346813516

1346913517
/**

0 commit comments

Comments
 (0)