mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-17 11:24:29 -05:00
More precise property-overwritten-by-spread errors (#37192)
* More precise property-overwritten-by-spread errors Trying to do this check in getSpreadType just doesn't have enough information, so I moved it to checkObjectLiteral, which is a better place for issuing errors anyway. Unfortunately, the approach is kind of expensive in that it 1. creates a new map for each property and 2. iterates over all properties of the spread type, even if it's a union. I have some ideas to improve (1) that might work out. I'm not sure how bad (2) is since we're going to iterate over all properties of all constituents of a union. Fixes #36779 * another test and rename
This commit is contained in:
committed by
GitHub
parent
3046a54401
commit
b481dd4d4b
@@ -13175,7 +13175,7 @@ namespace ts {
|
||||
* this function should be called in a left folding style, with left = previous result of getSpreadType
|
||||
* and right = the new element to be spread.
|
||||
*/
|
||||
function getSpreadType(left: Type, right: Type, symbol: Symbol | undefined, objectFlags: ObjectFlags, readonly: boolean, isParentTypeNullable?: boolean): Type {
|
||||
function getSpreadType(left: Type, right: Type, symbol: Symbol | undefined, objectFlags: ObjectFlags, readonly: boolean): Type {
|
||||
if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) {
|
||||
return anyType;
|
||||
}
|
||||
@@ -13191,16 +13191,16 @@ namespace ts {
|
||||
if (left.flags & TypeFlags.Union) {
|
||||
const merged = tryMergeUnionOfObjectTypeAndEmptyObject(left as UnionType, readonly);
|
||||
if (merged) {
|
||||
return getSpreadType(merged, right, symbol, objectFlags, readonly, isParentTypeNullable);
|
||||
return getSpreadType(merged, right, symbol, objectFlags, readonly);
|
||||
}
|
||||
return mapType(left, t => getSpreadType(t, right, symbol, objectFlags, readonly, isParentTypeNullable));
|
||||
return mapType(left, t => getSpreadType(t, right, symbol, objectFlags, readonly));
|
||||
}
|
||||
if (right.flags & TypeFlags.Union) {
|
||||
const merged = tryMergeUnionOfObjectTypeAndEmptyObject(right as UnionType, readonly);
|
||||
if (merged) {
|
||||
return getSpreadType(left, merged, symbol, objectFlags, readonly, maybeTypeOfKind(right, TypeFlags.Nullable));
|
||||
return getSpreadType(left, merged, symbol, objectFlags, readonly);
|
||||
}
|
||||
return mapType(right, t => getSpreadType(left, t, symbol, objectFlags, readonly, maybeTypeOfKind(right, TypeFlags.Nullable)));
|
||||
return mapType(right, t => getSpreadType(left, t, symbol, objectFlags, readonly));
|
||||
}
|
||||
if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)) {
|
||||
return left;
|
||||
@@ -13264,14 +13264,6 @@ namespace ts {
|
||||
result.nameType = getSymbolLinks(leftProp).nameType;
|
||||
members.set(leftProp.escapedName, result);
|
||||
}
|
||||
else if (strictNullChecks &&
|
||||
!isParentTypeNullable &&
|
||||
symbol &&
|
||||
!isFromSpreadAssignment(leftProp, symbol) &&
|
||||
isFromSpreadAssignment(rightProp, symbol) &&
|
||||
!maybeTypeOfKind(rightType, TypeFlags.Nullable)) {
|
||||
error(leftProp.valueDeclaration, Diagnostics._0_is_specified_more_than_once_so_this_usage_will_be_overwritten, unescapeLeadingUnderscores(leftProp.escapedName));
|
||||
}
|
||||
}
|
||||
else {
|
||||
members.set(leftProp.escapedName, getSpreadSymbol(leftProp, readonly));
|
||||
@@ -16802,10 +16794,6 @@ namespace ts {
|
||||
return match === -1 || discriminable.indexOf(/*searchElement*/ true, match + 1) !== -1 ? defaultValue : target.types[match];
|
||||
}
|
||||
|
||||
function isFromSpreadAssignment(prop: Symbol, container: Symbol) {
|
||||
return prop.valueDeclaration?.parent !== container.valueDeclaration;
|
||||
}
|
||||
|
||||
/**
|
||||
* A type is 'weak' if it is an object type with at least one optional property
|
||||
* and no required properties, call/construct signatures or index signatures
|
||||
@@ -22566,6 +22554,7 @@ namespace ts {
|
||||
checkGrammarObjectLiteralExpression(node, inDestructuringPattern);
|
||||
|
||||
let propertiesTable: SymbolTable;
|
||||
const allPropertiesTable = createSymbolTable();
|
||||
let propertiesArray: Symbol[] = [];
|
||||
let spread: Type = emptyObjectType;
|
||||
|
||||
@@ -22647,6 +22636,7 @@ namespace ts {
|
||||
prop.type = type;
|
||||
prop.target = member;
|
||||
member = prop;
|
||||
allPropertiesTable.set(prop.escapedName, prop);
|
||||
}
|
||||
else if (memberDecl.kind === SyntaxKind.SpreadAssignment) {
|
||||
if (languageVersion < ScriptTarget.ES2015) {
|
||||
@@ -22664,6 +22654,16 @@ namespace ts {
|
||||
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
|
||||
return errorType;
|
||||
}
|
||||
for (const right of getPropertiesOfType(type)) {
|
||||
const rightType = getTypeOfSymbol(right);
|
||||
const left = allPropertiesTable.get(right.escapedName);
|
||||
if (strictNullChecks &&
|
||||
left &&
|
||||
!maybeTypeOfKind(rightType, TypeFlags.Nullable)) {
|
||||
error(left.valueDeclaration, Diagnostics._0_is_specified_more_than_once_so_this_usage_will_be_overwritten, unescapeLeadingUnderscores(left.escapedName));
|
||||
}
|
||||
}
|
||||
|
||||
spread = getSpreadType(spread, type, node.symbol, objectFlags, inConstContext);
|
||||
offset = i + 1;
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user