@@ -8667,29 +8667,6 @@ namespace ts {
8667
8667
return Ternary.False;
8668
8668
}
8669
8669
8670
- // Check if a property with the given name is known anywhere in the given type. In an object type, a property
8671
- // is considered known if the object type is empty and the check is for assignability, if the object type has
8672
- // index signatures, or if the property is actually declared in the object type. In a union or intersection
8673
- // type, a property is considered known if it is known in any constituent type.
8674
- function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
8675
- if (type.flags & TypeFlags.Object) {
8676
- const resolved = resolveStructuredTypeMembers(<ObjectType>type);
8677
- if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
8678
- getPropertyOfType(type, name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
8679
- // For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
8680
- return true;
8681
- }
8682
- }
8683
- else if (type.flags & TypeFlags.UnionOrIntersection) {
8684
- for (const t of (<UnionOrIntersectionType>type).types) {
8685
- if (isKnownProperty(t, name, isComparingJsxAttributes)) {
8686
- return true;
8687
- }
8688
- }
8689
- }
8690
- return false;
8691
- }
8692
-
8693
8670
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
8694
8671
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
8695
8672
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
@@ -13838,21 +13815,26 @@ namespace ts {
13838
13815
checkJsxAttributesAssignableToTagNameAttributes(node);
13839
13816
}
13840
13817
13841
- // Check if a property with the given name is known anywhere in the given type. In an object type, a property
13842
- // is considered known if the object type is empty and the check is for assignability, if the object type has
13843
- // index signatures, or if the property is actually declared in the object type. In a union or intersection
13844
- // type, a property is considered known if it is known in any constituent type.
13845
- function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
13846
- if (type.flags & TypeFlags.Object) {
13847
- const resolved = resolveStructuredTypeMembers(<ObjectType>type);
13818
+ /**
13819
+ * Check if a property with the given name is known anywhere in the given type. In an object type, a property
13820
+ * is considered known if the object type is empty and the check is for assignability, if the object type has
13821
+ * index signatures, or if the property is actually declared in the object type. In a union or intersection
13822
+ * type, a property is considered known if it is known in any constituent type.
13823
+ * @param targetType a type to search a given name in
13824
+ * @param name a property name to search
13825
+ * @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType
13826
+ */
13827
+ function isKnownProperty(targetType: Type, name: string, isComparingJsxAttributes: boolean): boolean {
13828
+ if (targetType.flags & TypeFlags.Object) {
13829
+ const resolved = resolveStructuredTypeMembers(<ObjectType>targetType);
13848
13830
if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
13849
- getPropertyOfType(type , name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
13831
+ getPropertyOfType(targetType , name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
13850
13832
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
13851
13833
return true;
13852
13834
}
13853
13835
}
13854
- else if (type .flags & TypeFlags.UnionOrIntersection) {
13855
- for (const t of (<UnionOrIntersectionType>type ).types) {
13836
+ else if (targetType .flags & TypeFlags.UnionOrIntersection) {
13837
+ for (const t of (<UnionOrIntersectionType>targetType ).types) {
13856
13838
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
13857
13839
return true;
13858
13840
}
@@ -13861,7 +13843,7 @@ namespace ts {
13861
13843
return false;
13862
13844
}
13863
13845
13864
- /**
13846
+ /**
13865
13847
* Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes.
13866
13848
* Get the attributes type of the opening-like element through resolving the tagName, "target attributes"
13867
13849
* Check assignablity between given attributes property, "source attributes", and the "target attributes"
@@ -13893,14 +13875,16 @@ namespace ts {
13893
13875
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
13894
13876
}
13895
13877
else {
13878
+ // Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
13896
13879
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
13897
- // After we check for assignability, we will do another pass to check that
13898
- // all explicitly specified attributes have correct name corresponding with target (as those will be assignable as spread type allows excess properties)
13899
- // Note: if the type of these explicitly specified attributes do not match it will be an error during above assignability check.
13880
+ // After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
13881
+ // 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.
13900
13882
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
13901
13883
for (const attribute of openingLikeElement.attributes.properties) {
13902
13884
if (isJsxAttribute(attribute) && !isKnownProperty(targetAttributesType, attribute.name.text, /*isComparingJsxAttributes*/ true)) {
13903
13885
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, attribute.name.text, typeToString(targetAttributesType));
13886
+ // We break here so that errors won't be cascading
13887
+ break;
13904
13888
}
13905
13889
}
13906
13890
}
0 commit comments