1- /// <reference path="moduleNameResolver.ts"/>
1+ /// <reference path="moduleNameResolver.ts"/>
22/// <reference path="binder.ts"/>
33
44/* @internal */
@@ -424,6 +424,8 @@ namespace ts {
424424 IntrinsicClassAttributes: "IntrinsicClassAttributes"
425425 };
426426
427+ const jsxChildrenPropertyName = "children";
428+
427429 const subtypeRelation = createMap<RelationComparisonResult>();
428430 const assignableRelation = createMap<RelationComparisonResult>();
429431 const comparableRelation = createMap<RelationComparisonResult>();
@@ -8648,7 +8650,7 @@ namespace ts {
86488650 // is considered known if the object type is empty and the check is for assignability, if the object type has
86498651 // index signatures, or if the property is actually declared in the object type. In a union or intersection
86508652 // type, a property is considered known if it is known in any constituent type.
8651- function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
8653+ function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean, containsSynthesizedJsxChildren: boolean ): boolean {
86528654 if (type.flags & TypeFlags.Object) {
86538655 const resolved = resolveStructuredTypeMembers(<ObjectType>type);
86548656 if ((relation === assignableRelation || relation === comparableRelation) &&
@@ -8658,14 +8660,16 @@ namespace ts {
86588660 else if (resolved.stringIndexInfo || (resolved.numberIndexInfo && isNumericLiteralName(name))) {
86598661 return true;
86608662 }
8661- else if (getPropertyOfType(type, name) || (isComparingJsxAttributes && !isUnhyphenatedJsxName(name))) {
8662- // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
8663+ else if (getPropertyOfType(type, name) || containsSynthesizedJsxChildren || (isComparingJsxAttributes && !isUnhyphenatedJsxName(name))) {
8664+ // For JSXAttributes, consider that the attribute to be known if
8665+ // 1. the attribute has a hyphenated name
8666+ // 2. "children" attribute that is synthesized from children property of Jsx element
86638667 return true;
86648668 }
86658669 }
86668670 else if (type.flags & TypeFlags.UnionOrIntersection) {
86678671 for (const t of (<UnionOrIntersectionType>type).types) {
8668- if (isKnownProperty(t, name, isComparingJsxAttributes)) {
8672+ if (isKnownProperty(t, name, isComparingJsxAttributes, containsSynthesizedJsxChildren )) {
86698673 return true;
86708674 }
86718675 }
@@ -8676,8 +8680,9 @@ namespace ts {
86768680 function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
86778681 if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
86788682 const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
8683+ const containsSynthesizedJsxChildren = !!(source.flags & TypeFlags.ContainsSynthesizedJsxChildren);
86798684 for (const prop of getPropertiesOfObjectType(source)) {
8680- if (!isKnownProperty(target, prop.name, isComparingJsxAttributes)) {
8685+ if (!isKnownProperty(target, prop.name, isComparingJsxAttributes, containsSynthesizedJsxChildren )) {
86818686 if (reportErrors) {
86828687 // We know *exactly* where things went wrong when comparing the types.
86838688 // Use this property as the error node as this will be more helpful in
@@ -13173,21 +13178,6 @@ namespace ts {
1317313178 checkExpression(node.closingElement.tagName);
1317413179 }
1317513180
13176- // Check children
13177- for (const child of node.children) {
13178- switch (child.kind) {
13179- case SyntaxKind.JsxExpression:
13180- checkJsxExpression(<JsxExpression>child);
13181- break;
13182- case SyntaxKind.JsxElement:
13183- checkJsxElement(<JsxElement>child);
13184- break;
13185- case SyntaxKind.JsxSelfClosingElement:
13186- checkJsxSelfClosingElement(<JsxSelfClosingElement>child);
13187- break;
13188- }
13189- }
13190-
1319113181 return getJsxGlobalElementType() || anyType;
1319213182 }
1319313183
@@ -13280,6 +13270,30 @@ namespace ts {
1328013270 }
1328113271 });
1328213272 }
13273+
13274+ // Handle children attribute
13275+ const parent = openingLikeElement.parent.kind === SyntaxKind.JsxElement ?
13276+ openingLikeElement.parent as JsxElement : undefined;
13277+ let containsSynthesizedJsxChildren = false;
13278+ // Comment
13279+ if (parent && parent.openingElement === openingLikeElement && parent.children.length > 0) {
13280+ // Error if there is a attribute named "children" and children element.
13281+ // This is because children element will overwrite the value from attributes
13282+ if (attributesTable.has(jsxChildrenPropertyName)) {
13283+ error(attributes, Diagnostics.props_children_are_specified_twice_The_attribute_named_children_will_be_overwritten);
13284+ }
13285+
13286+ // 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
13287+ const childrenPropSymbol = createSymbol(SymbolFlags.Property | SymbolFlags.Transient, jsxChildrenPropertyName);
13288+ const childrenTypes: Type[] = [];
13289+ for (const child of (parent as JsxElement).children) {
13290+ childrenTypes.push(child.kind === SyntaxKind.JsxText ? stringType : checkExpression(child));
13291+ }
13292+ childrenPropSymbol.type = getUnionType(childrenTypes, /*subtypeReduction*/ false);
13293+ attributesTable.set(jsxChildrenPropertyName, childrenPropSymbol);
13294+ containsSynthesizedJsxChildren = true;
13295+ }
13296+
1328313297 return createJsxAttributesType(attributes.symbol, attributesTable);
1328413298
1328513299 /**
@@ -13290,7 +13304,8 @@ namespace ts {
1329013304 function createJsxAttributesType(symbol: Symbol, attributesTable: Map<Symbol>) {
1329113305 const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
1329213306 const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral;
13293- result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag;
13307+ const containsSynthesizedJsxChildrenFlag = containsSynthesizedJsxChildren ? TypeFlags.ContainsSynthesizedJsxChildren : 0;
13308+ result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag | containsSynthesizedJsxChildrenFlag;
1329413309 result.objectFlags |= ObjectFlags.ObjectLiteral;
1329513310 return result;
1329613311 }
@@ -14533,7 +14548,7 @@ namespace ts {
1453314548 // We can figure that out by resolving attributes property and check number of properties in the resolved type
1453414549 // If the call has correct arity, we will then check if the argument type and parameter type is assignable
1453514550
14536- const callIsIncomplete = node.attributes.end === node.end; // If we are missing the close "/>", the call is incomplete
14551+ const callIsIncomplete = node.attributes.end === node.end; // If we are missing the close "/>", the call is incoplete
1453714552 if (callIsIncomplete) {
1453814553 return true;
1453914554 }
0 commit comments