Skip to content

Commit f9dbfd4

Browse files
author
Kanchalai Tanglertsampan
committed
Allow excess property in spread attributes but also check if any explicitly specified attributes are correct
1 parent 5e3fefd commit f9dbfd4

File tree

1 file changed

+11
-7
lines changed

1 file changed

+11
-7
lines changed

src/compiler/checker.ts

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13293,10 +13293,11 @@ namespace ts {
1329313293
}
1329413294
}
1329513295

13296-
// Error if there is a attribute named "children" and children element.
13297-
// This is because children element will overwrite the value from attributes
13298-
if (explicitlySpecifyChildrenAttribute) {
13299-
if (attributesTable.has(jsxChildrenPropertyName)) {
13296+
if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
13297+
// Error if there is a attribute named "children" explicitly specified and children element.
13298+
// This is because children element will overwrite the value from attributes.
13299+
// Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread.
13300+
if (explicitlySpecifyChildrenAttribute) {
1330013301
error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName);
1330113302
}
1330213303

@@ -13318,6 +13319,7 @@ namespace ts {
1331813319
*/
1331913320
function createJsxAttributesType(symbol: Symbol, attributesTable: Map<Symbol>) {
1332013321
const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
13322+
// Spread object doesn't have freshness flag to allow excess attributes as it is very common for parent component to spread its "props" to other components in its render method.
1332113323
const freshObjectLiteralFlag = spread !== emptyObjectType || compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral;
1332213324
result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag;
1332313325
result.objectFlags |= ObjectFlags.ObjectLiteral;
@@ -13870,9 +13872,11 @@ namespace ts {
1387013872
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
1387113873
}
1387213874
else {
13873-
const isAssignableToTargetAttributes = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
13874-
// TODO (yuisu): comment
13875-
if (isAssignableToTargetAttributes && sourceAttributesType !== anyType && !(sourceAttributesType.flags & TypeFlags.FreshLiteral)) {
13875+
checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
13876+
// If sourceAttributesType has spread (e.g the type doesn't have freshness flag) after we check for assignability, we will do another pass to check that
13877+
// all explicitly specified attributes have correct name corresponding with target (as those will be assignable as spread type allows excess properties)
13878+
// Note: if the type of these explicitly specified attributes do not match it will be an error during above assignability check.
13879+
if (sourceAttributesType !== anyType && !(sourceAttributesType.flags & TypeFlags.FreshLiteral)) {
1387613880
for (const attribute of openingLikeElement.attributes.properties) {
1387713881
if (isJsxAttribute(attribute) && !getPropertyOfType(targetAttributesType, attribute.name.text)) {
1387813882
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, attribute.name.text, typeToString(targetAttributesType));

0 commit comments

Comments
 (0)