mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-15 21:36:50 -05:00
Merge pull request #15595 from Microsoft/master-fix15463
[Master] fix 15463
This commit is contained in:
@@ -8690,29 +8690,6 @@ namespace ts {
|
||||
return Ternary.False;
|
||||
}
|
||||
|
||||
// Check if a property with the given name is known anywhere in the given type. In an object type, a property
|
||||
// is considered known if the object type is empty and the check is for assignability, if the object type has
|
||||
// index signatures, or if the property is actually declared in the object type. In a union or intersection
|
||||
// type, a property is considered known if it is known in any constituent type.
|
||||
function isKnownProperty(type: Type, name: string, isComparingJsxAttributes: boolean): boolean {
|
||||
if (type.flags & TypeFlags.Object) {
|
||||
const resolved = resolveStructuredTypeMembers(<ObjectType>type);
|
||||
if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
|
||||
getPropertyOfType(type, name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
|
||||
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (type.flags & TypeFlags.UnionOrIntersection) {
|
||||
for (const t of (<UnionOrIntersectionType>type).types) {
|
||||
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function hasExcessProperties(source: FreshObjectLiteralType, target: Type, reportErrors: boolean): boolean {
|
||||
if (maybeTypeOfKind(target, TypeFlags.Object) && !(getObjectFlags(target) & ObjectFlags.ObjectLiteralPatternWithComputedProperties)) {
|
||||
const isComparingJsxAttributes = !!(source.flags & TypeFlags.JsxAttributes);
|
||||
@@ -13276,6 +13253,8 @@ namespace ts {
|
||||
let spread: Type = emptyObjectType;
|
||||
let attributesArray: Symbol[] = [];
|
||||
let hasSpreadAnyType = false;
|
||||
let explicitlySpecifyChildrenAttribute = false;
|
||||
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
|
||||
|
||||
for (const attributeDecl of attributes.properties) {
|
||||
const member = attributeDecl.symbol;
|
||||
@@ -13294,6 +13273,9 @@ namespace ts {
|
||||
attributeSymbol.target = member;
|
||||
attributesTable.set(attributeSymbol.name, attributeSymbol);
|
||||
attributesArray.push(attributeSymbol);
|
||||
if (attributeDecl.name.text === jsxChildrenPropertyName) {
|
||||
explicitlySpecifyChildrenAttribute = true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.assert(attributeDecl.kind === SyntaxKind.JsxSpreadAttribute);
|
||||
@@ -13352,11 +13334,11 @@ namespace ts {
|
||||
}
|
||||
}
|
||||
|
||||
// Error if there is a attribute named "children" and children element.
|
||||
// This is because children element will overwrite the value from attributes
|
||||
const jsxChildrenPropertyName = getJsxElementChildrenPropertyname();
|
||||
if (!hasSpreadAnyType && jsxChildrenPropertyName && jsxChildrenPropertyName !== "") {
|
||||
if (attributesTable.has(jsxChildrenPropertyName)) {
|
||||
// Error if there is a attribute named "children" explicitly specified and children element.
|
||||
// This is because children element will overwrite the value from attributes.
|
||||
// Note: we will not warn "children" attribute overwritten if "children" attribute is specified in object spread.
|
||||
if (explicitlySpecifyChildrenAttribute) {
|
||||
error(attributes, Diagnostics._0_are_specified_twice_The_attribute_named_0_will_be_overwritten, jsxChildrenPropertyName);
|
||||
}
|
||||
|
||||
@@ -13378,8 +13360,7 @@ namespace ts {
|
||||
*/
|
||||
function createJsxAttributesType(symbol: Symbol, attributesTable: Map<Symbol>) {
|
||||
const result = createAnonymousType(symbol, attributesTable, emptyArray, emptyArray, /*stringIndexInfo*/ undefined, /*numberIndexInfo*/ undefined);
|
||||
const freshObjectLiteralFlag = compilerOptions.suppressExcessPropertyErrors ? 0 : TypeFlags.FreshLiteral;
|
||||
result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral | freshObjectLiteralFlag;
|
||||
result.flags |= TypeFlags.JsxAttributes | TypeFlags.ContainsObjectLiteral;
|
||||
result.objectFlags |= ObjectFlags.ObjectLiteral;
|
||||
return result;
|
||||
}
|
||||
@@ -13898,6 +13879,34 @@ namespace ts {
|
||||
checkJsxAttributesAssignableToTagNameAttributes(node);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a property with the given name is known anywhere in the given type. In an object type, a property
|
||||
* is considered known if the object type is empty and the check is for assignability, if the object type has
|
||||
* index signatures, or if the property is actually declared in the object type. In a union or intersection
|
||||
* type, a property is considered known if it is known in any constituent type.
|
||||
* @param targetType a type to search a given name in
|
||||
* @param name a property name to search
|
||||
* @param isComparingJsxAttributes a boolean flag indicating whether we are searching in JsxAttributesType
|
||||
*/
|
||||
function isKnownProperty(targetType: Type, name: string, isComparingJsxAttributes: boolean): boolean {
|
||||
if (targetType.flags & TypeFlags.Object) {
|
||||
const resolved = resolveStructuredTypeMembers(<ObjectType>targetType);
|
||||
if (resolved.stringIndexInfo || resolved.numberIndexInfo && isNumericLiteralName(name) ||
|
||||
getPropertyOfType(targetType, name) || isComparingJsxAttributes && !isUnhyphenatedJsxName(name)) {
|
||||
// For JSXAttributes, if the attribute has a hyphenated name, consider that the attribute to be known.
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (targetType.flags & TypeFlags.UnionOrIntersection) {
|
||||
for (const t of (<UnionOrIntersectionType>targetType).types) {
|
||||
if (isKnownProperty(t, name, isComparingJsxAttributes)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the given attributes of JSX opening-like element is assignable to the tagName attributes.
|
||||
* Get the attributes type of the opening-like element through resolving the tagName, "target attributes"
|
||||
@@ -13930,7 +13939,19 @@ namespace ts {
|
||||
error(openingLikeElement, Diagnostics.JSX_element_class_does_not_support_attributes_because_it_does_not_have_a_0_property, getJsxElementPropertiesName());
|
||||
}
|
||||
else {
|
||||
checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
|
||||
// Check if sourceAttributesType assignable to targetAttributesType though this check will allow excess properties
|
||||
const isSourceAttributeTypeAssignableToTarget = checkTypeAssignableTo(sourceAttributesType, targetAttributesType, openingLikeElement.attributes.properties.length > 0 ? openingLikeElement.attributes : openingLikeElement);
|
||||
// After we check for assignability, we will do another pass to check that all explicitly specified attributes have correct name corresponding in targetAttributeType.
|
||||
// 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.
|
||||
if (isSourceAttributeTypeAssignableToTarget && !isTypeAny(sourceAttributesType) && !isTypeAny(targetAttributesType)) {
|
||||
for (const attribute of openingLikeElement.attributes.properties) {
|
||||
if (isJsxAttribute(attribute) && !isKnownProperty(targetAttributesType, attribute.name.text, /*isComparingJsxAttributes*/ true)) {
|
||||
error(attribute, Diagnostics.Property_0_does_not_exist_on_type_1, attribute.name.text, typeToString(targetAttributesType));
|
||||
// We break here so that errors won't be cascading
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user