1
- /// <reference path="moduleNameResolver.ts"/>
1
+ /// <reference path="moduleNameResolver.ts"/>
2
2
/// <reference path="binder.ts"/>
3
3
4
4
/* @internal */
@@ -424,6 +424,8 @@ namespace ts {
424
424
IntrinsicClassAttributes: "IntrinsicClassAttributes"
425
425
};
426
426
427
+ const jsxChildrenPropertyName = "children";
428
+
427
429
const subtypeRelation = createMap<RelationComparisonResult>();
428
430
const assignableRelation = createMap<RelationComparisonResult>();
429
431
const comparableRelation = createMap<RelationComparisonResult>();
@@ -8648,7 +8650,7 @@ namespace ts {
8648
8650
// is considered known if the object type is empty and the check is for assignability, if the object type has
8649
8651
// index signatures, or if the property is actually declared in the object type. In a union or intersection
8650
8652
// 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 {
8652
8654
if (type.flags & TypeFlags.Object) {
8653
8655
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
8654
8656
if ((relation === assignableRelation || relation === comparableRelation) &&
@@ -8658,14 +8660,16 @@ namespace ts {
8658
8660
else if (resolved.stringIndexInfo || (resolved.numberIndexInfo && isNumericLiteralName(name))) {
8659
8661
return true;
8660
8662
}
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
8663
8667
return true;
8664
8668
}
8665
8669
}
8666
8670
else if (type.flags & TypeFlags.UnionOrIntersection) {
8667
8671
for (const t of (<UnionOrIntersectionType>type).types) {
8668
- if (isKnownProperty(t, name, isComparingJsxAttributes)) {
8672
+ if (isKnownProperty(t, name, isComparingJsxAttributes, containsSynthesizedJsxChildren )) {
8669
8673
return true;
8670
8674
}
8671
8675
}
@@ -8676,8 +8680,9 @@ namespace ts {
8676
8680
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
8677
8681
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
8678
8682
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
8683
+ const containsSynthesizedJsxChildren = !!(source.flags & TypeFlags.ContainsSynthesizedJsxChildren);
8679
8684
for (const prop of getPropertiesOfObjectType(source)) {
8680
- if (!isKnownProperty(target, prop.name, isComparingJsxAttributes)) {
8685
+ if (!isKnownProperty(target, prop.name, isComparingJsxAttributes, containsSynthesizedJsxChildren )) {
8681
8686
if (reportErrors) {
8682
8687
// We know *exactly* where things went wrong when comparing the types.
8683
8688
// Use this property as the error node as this will be more helpful in
@@ -13173,21 +13178,6 @@ namespace ts {
13173
13178
checkExpression(node.closingElement.tagName);
13174
13179
}
13175
13180
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
-
13191
13181
return getJsxGlobalElementType() || anyType;
13192
13182
}
13193
13183
@@ -13280,6 +13270,30 @@ namespace ts {
13280
13270
}
13281
13271
});
13282
13272
}
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
+
13283
13297
return createJsxAttributesType(attributes.symbol, attributesTable);
13284
13298
13285
13299
/**
@@ -13290,7 +13304,8 @@ namespace ts {
13290
13304
function createJsxAttributesType(symbol: Symbol, attributesTable: Map<Symbol>) {
13291
13305
const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
13292
13306
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;
13294
13309
result.objectFlags |= ObjectFlags.ObjectLiteral;
13295
13310
return result;
13296
13311
}
@@ -14533,7 +14548,7 @@ namespace ts {
14533
14548
// We can figure that out by resolving attributes property and check number of properties in the resolved type
14534
14549
// If the call has correct arity, we will then check if the argument type and parameter type is assignable
14535
14550
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
14537
14552
if (callIsIncomplete) {
14538
14553
return true;
14539
14554
}
0 commit comments