@@ -8688,29 +8688,6 @@ namespace ts {
8688
8688
return Ternary.False;
8689
8689
}
8690
8690
8691
- // Check if a property with the given name is known anywhere in the given type. In an object type, a property
8692
- // is considered known if the object type is empty and the check is for assignability, if the object type has
8693
- // index signatures, or if the property is actually declared in the object type. In a union or intersection
8694
- // type, a property is considered known if it is known in any constituent type.
8695
- function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
8696
- if (type.flags & TypeFlags.Object) {
8697
- const resolved = resolveStructuredTypeMembers(<ObjectType>type);
8698
- if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
8699
- getPropertyOfType(type, name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
8700
- // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
8701
- return true;
8702
- }
8703
- }
8704
- else if (type.flags & TypeFlags.UnionOrIntersection) {
8705
- for (const t of (<UnionOrIntersectionType>type).types) {
8706
- if (isKnownProperty(t, name, isComparingJsxAttributes)) {
8707
- return true;
8708
- }
8709
- }
8710
- }
8711
- return false;
8712
- }
8713
-
8714
8691
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
8715
8692
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
8716
8693
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
@@ -13875,21 +13852,26 @@ namespace ts {
13875
13852
checkJsxAttributesAssignableToTagNameAttributes(node);
13876
13853
}
13877
13854
13878
- // Check if a property with the given name is known anywhere in the given type. In an object type, a property
13879
- // is considered known if the object type is empty and the check is for assignability, if the object type has
13880
- // index signatures, or if the property is actually declared in the object type. In a union or intersection
13881
- // type, a property is considered known if it is known in any constituent type.
13882
- function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
13883
- if (type.flags & TypeFlags.Object) {
13884
- const resolved = resolveStructuredTypeMembers(<ObjectType>type);
13855
+ /**
13856
+ * Check if a property with the given name is known anywhere in the given type. In an object type, a property
13857
+ * is considered known if the object type is empty and the check is for assignability, if the object type has
13858
+ * index signatures, or if the property is actually declared in the object type. In a union or intersection
13859
+ * type, a property is considered known if it is known in any constituent type.
13860
+ * @param targetType a type to search a given name in
13861
+ * @param name a property name to search
13862
+ * @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType
13863
+ */
13864
+ function isKnownProperty(targetType: Type, name: string, isComparingJsxAttributes: boolean): boolean {
13865
+ if (targetType.flags & TypeFlags.Object) {
13866
+ const resolved = resolveStructuredTypeMembers(<ObjectType>targetType);
13885
13867
if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
13886
- getPropertyOfType(type , name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
13868
+ getPropertyOfType(targetType , name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
13887
13869
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
13888
13870
return true;
13889
13871
}
13890
13872
}
13891
- else if (type .flags & TypeFlags.UnionOrIntersection) {
13892
- for (const t of (<UnionOrIntersectionType>type ).types) {
13873
+ else if (targetType .flags & TypeFlags.UnionOrIntersection) {
13874
+ for (const t of (<UnionOrIntersectionType>targetType ).types) {
13893
13875
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
13894
13876
return true;
13895
13877
}
@@ -13898,7 +13880,7 @@ namespace ts {
13898
13880
return false;
13899
13881
}
13900
13882
13901
- /**
13883
+ /**
13902
13884
* Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes.
13903
13885
* Get the attributes type of the opening-like element through resolving the tagName, "target attributes"
13904
13886
* Check assignablity between given attributes property, "source attributes", and the "target attributes"
@@ -13930,14 +13912,16 @@ namespace ts {
13930
13912
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
13931
13913
}
13932
13914
else {
13915
+ // Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
13933
13916
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
13934
- // After we check for assignability, we will do another pass to check that
13935
- // all explicitly specified attributes have correct name corresponding with target (as those will be assignable as spread type allows excess properties)
13936
- // Note: if the type of these explicitly specified attributes do not match it will be an error during above assignability check.
13917
+ // After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
13918
+ // This will allow excess properties in spread type as it is very common pattern to spread outter attributes into React component in its render method.
13937
13919
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
13938
13920
for (const attribute of openingLikeElement.attributes.properties) {
13939
13921
if (isJsxAttribute(attribute) && !isKnownProperty(targetAttributesType, attribute.name.text, /*isComparingJsxAttributes*/ true)) {
13940
13922
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, attribute.name.text, typeToString(targetAttributesType));
13923
+ // We break here so that errors won't be cascading
13924
+ break;
13941
13925
}
13942
13926
}
13943
13927
}
0 commit comments