Fix check for overwritten properties in object spreads (#44696)

* Fix check for overwritten properties in object spreads

* Accept new baselines

* Add tests

* Accept new baselines
This commit is contained in:
Anders Hejlsberg 2021-06-22 14:39:33 -07:00 committed by GitHub
parent 22637a232b
commit 61ccc49a7b
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 616 additions and 24 deletions

View file

@ -15368,17 +15368,20 @@ namespace ts {
return isEmptyObjectType(type) || !!(type.flags & (TypeFlags.Null | TypeFlags.Undefined | TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.BigIntLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index));
}
function tryMergeUnionOfObjectTypeAndEmptyObject(type: UnionType, readonly: boolean): Type | undefined {
if (every(type.types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) {
return find(type.types, isEmptyObjectType) || emptyObjectType;
function tryMergeUnionOfObjectTypeAndEmptyObject(type: Type, readonly: boolean): Type {
if (!(type.flags & TypeFlags.Union)) {
return type;
}
const firstType = find(type.types, t => !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t));
if (every((type as UnionType).types, isEmptyObjectTypeOrSpreadsIntoEmptyObject)) {
return find((type as UnionType).types, isEmptyObjectType) || emptyObjectType;
}
const firstType = find((type as UnionType).types, t => !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t));
if (!firstType) {
return undefined;
return type;
}
const secondType = firstType && find(type.types, t => t !== firstType && !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t));
const secondType = find((type as UnionType).types, t => t !== firstType && !isEmptyObjectTypeOrSpreadsIntoEmptyObject(t));
if (secondType) {
return undefined;
return type;
}
return getAnonymousPartialType(firstType);
@ -15424,20 +15427,14 @@ namespace ts {
if (right.flags & TypeFlags.Never) {
return left;
}
left = tryMergeUnionOfObjectTypeAndEmptyObject(left, readonly);
if (left.flags & TypeFlags.Union) {
const merged = tryMergeUnionOfObjectTypeAndEmptyObject(left as UnionType, readonly);
if (merged) {
return getSpreadType(merged, right, symbol, objectFlags, readonly);
}
return checkCrossProductUnion([left, right])
? mapType(left, t => getSpreadType(t, right, symbol, objectFlags, readonly))
: errorType;
}
right = tryMergeUnionOfObjectTypeAndEmptyObject(right, readonly);
if (right.flags & TypeFlags.Union) {
const merged = tryMergeUnionOfObjectTypeAndEmptyObject(right as UnionType, readonly);
if (merged) {
return getSpreadType(left, merged, symbol, objectFlags, readonly);
}
return checkCrossProductUnion([left, right])
? mapType(right, t => getSpreadType(left, t, symbol, objectFlags, readonly))
: errorType;
@ -15487,7 +15484,7 @@ namespace ts {
const declarations = concatenate(leftProp.declarations, rightProp.declarations);
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
const result = createSymbol(flags, leftProp.escapedName);
result.type = getUnionType([getTypeOfSymbol(leftProp), getTypeWithFacts(rightType, TypeFacts.NEUndefined)]);
result.type = getUnionType([getTypeOfSymbol(leftProp), removeMissingOrUndefinedType(rightType)]);
result.leftSpread = leftProp;
result.rightSpread = rightProp;
result.declarations = declarations;
@ -26310,14 +26307,15 @@ namespace ts {
}
const type = getReducedType(checkExpression(memberDecl.expression));
if (isValidSpreadType(type)) {
const mergedType = tryMergeUnionOfObjectTypeAndEmptyObject(type, inConstContext);
if (allPropertiesTable) {
checkSpreadPropOverrides(type, allPropertiesTable, memberDecl);
checkSpreadPropOverrides(mergedType, allPropertiesTable, memberDecl);
}
offset = propertiesArray.length;
if (spread === errorType) {
continue;
}
spread = getSpreadType(spread, type, node.symbol, objectFlags, inConstContext);
spread = getSpreadType(spread, mergedType, node.symbol, objectFlags, inConstContext);
}
else {
error(memberDecl, Diagnostics.Spread_types_may_only_be_created_from_object_types);
@ -26632,11 +26630,12 @@ namespace ts {
function checkSpreadPropOverrides(type: Type, props: SymbolTable, spread: SpreadAssignment | JsxSpreadAttribute) {
for (const right of getPropertiesOfType(type)) {
const left = props.get(right.escapedName);
const rightType = getTypeOfSymbol(right);
if (left && !maybeTypeOfKind(rightType, TypeFlags.Nullable) && !(maybeTypeOfKind(rightType, TypeFlags.AnyOrUnknown) && right.flags & SymbolFlags.Optional)) {
const diagnostic = error(left.valueDeclaration, Diagnostics._0_is_specified_more_than_once_so_this_usage_will_be_overwritten, unescapeLeadingUnderscores(left.escapedName));
addRelatedInfo(diagnostic, createDiagnosticForNode(spread, Diagnostics.This_spread_always_overwrites_this_property));
if (!(right.flags & SymbolFlags.Optional)) {
const left = props.get(right.escapedName);
if (left) {
const diagnostic = error(left.valueDeclaration, Diagnostics._0_is_specified_more_than_once_so_this_usage_will_be_overwritten, unescapeLeadingUnderscores(left.escapedName));
addRelatedInfo(diagnostic, createDiagnosticForNode(spread, Diagnostics.This_spread_always_overwrites_this_property));
}
}
}
}

View file

@ -0,0 +1,30 @@
tests/cases/conformance/types/spread/spreadDuplicate.ts(10,12): error TS2783: 'a' is specified more than once, so this usage will be overwritten.
tests/cases/conformance/types/spread/spreadDuplicate.ts(12,12): error TS2783: 'a' is specified more than once, so this usage will be overwritten.
==== tests/cases/conformance/types/spread/spreadDuplicate.ts (2 errors) ====
// Repro from #44438
declare let a: { a: string };
declare let b: { a?: string };
declare let c: { a: string | undefined };
declare let d: { a?: string | undefined };
declare let t: boolean;
let a1 = { a: 123, ...a }; // string (Error)
~~~~~~
!!! error TS2783: 'a' is specified more than once, so this usage will be overwritten.
!!! related TS2785 tests/cases/conformance/types/spread/spreadDuplicate.ts:10:20: This spread always overwrites this property.
let b1 = { a: 123, ...b }; // string | number
let c1 = { a: 123, ...c }; // string | undefined (Error)
~~~~~~
!!! error TS2783: 'a' is specified more than once, so this usage will be overwritten.
!!! related TS2785 tests/cases/conformance/types/spread/spreadDuplicate.ts:12:20: This spread always overwrites this property.
let d1 = { a: 123, ...d }; // string | number
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
let c2 = { a: 123, ...(t ? c : {}) }; // string | number
let d2 = { a: 123, ...(t ? d : {}) }; // string | number

View file

@ -0,0 +1,83 @@
//// [spreadDuplicate.ts]
// Repro from #44438
declare let a: { a: string };
declare let b: { a?: string };
declare let c: { a: string | undefined };
declare let d: { a?: string | undefined };
declare let t: boolean;
let a1 = { a: 123, ...a }; // string (Error)
let b1 = { a: 123, ...b }; // string | number
let c1 = { a: 123, ...c }; // string | undefined (Error)
let d1 = { a: 123, ...d }; // string | number
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
let c2 = { a: 123, ...(t ? c : {}) }; // string | number
let d2 = { a: 123, ...(t ? d : {}) }; // string | number
//// [spreadDuplicate.js]
"use strict";
// Repro from #44438
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var a1 = __assign({ a: 123 }, a); // string (Error)
var b1 = __assign({ a: 123 }, b); // string | number
var c1 = __assign({ a: 123 }, c); // string | undefined (Error)
var d1 = __assign({ a: 123 }, d); // string | number
var a2 = __assign({ a: 123 }, (t ? a : {})); // string | number
var b2 = __assign({ a: 123 }, (t ? b : {})); // string | number
var c2 = __assign({ a: 123 }, (t ? c : {})); // string | number
var d2 = __assign({ a: 123 }, (t ? d : {})); // string | number
//// [spreadDuplicate.d.ts]
declare let a: {
a: string;
};
declare let b: {
a?: string;
};
declare let c: {
a: string | undefined;
};
declare let d: {
a?: string | undefined;
};
declare let t: boolean;
declare let a1: {
a: string;
};
declare let b1: {
a: string | number;
};
declare let c1: {
a: string | undefined;
};
declare let d1: {
a: string | number;
};
declare let a2: {
a: string | number;
};
declare let b2: {
a: string | number;
};
declare let c2: {
a: string | number;
};
declare let d2: {
a: string | number;
};

View file

@ -0,0 +1,66 @@
=== tests/cases/conformance/types/spread/spreadDuplicate.ts ===
// Repro from #44438
declare let a: { a: string };
>a : Symbol(a, Decl(spreadDuplicate.ts, 2, 11))
>a : Symbol(a, Decl(spreadDuplicate.ts, 2, 16))
declare let b: { a?: string };
>b : Symbol(b, Decl(spreadDuplicate.ts, 3, 11))
>a : Symbol(a, Decl(spreadDuplicate.ts, 3, 16))
declare let c: { a: string | undefined };
>c : Symbol(c, Decl(spreadDuplicate.ts, 4, 11))
>a : Symbol(a, Decl(spreadDuplicate.ts, 4, 16))
declare let d: { a?: string | undefined };
>d : Symbol(d, Decl(spreadDuplicate.ts, 5, 11))
>a : Symbol(a, Decl(spreadDuplicate.ts, 5, 16))
declare let t: boolean;
>t : Symbol(t, Decl(spreadDuplicate.ts, 7, 11))
let a1 = { a: 123, ...a }; // string (Error)
>a1 : Symbol(a1, Decl(spreadDuplicate.ts, 9, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 9, 10))
>a : Symbol(a, Decl(spreadDuplicate.ts, 2, 11))
let b1 = { a: 123, ...b }; // string | number
>b1 : Symbol(b1, Decl(spreadDuplicate.ts, 10, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 10, 10))
>b : Symbol(b, Decl(spreadDuplicate.ts, 3, 11))
let c1 = { a: 123, ...c }; // string | undefined (Error)
>c1 : Symbol(c1, Decl(spreadDuplicate.ts, 11, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 11, 10))
>c : Symbol(c, Decl(spreadDuplicate.ts, 4, 11))
let d1 = { a: 123, ...d }; // string | number
>d1 : Symbol(d1, Decl(spreadDuplicate.ts, 12, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 12, 10))
>d : Symbol(d, Decl(spreadDuplicate.ts, 5, 11))
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
>a2 : Symbol(a2, Decl(spreadDuplicate.ts, 14, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 14, 10))
>t : Symbol(t, Decl(spreadDuplicate.ts, 7, 11))
>a : Symbol(a, Decl(spreadDuplicate.ts, 2, 11))
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
>b2 : Symbol(b2, Decl(spreadDuplicate.ts, 15, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 15, 10))
>t : Symbol(t, Decl(spreadDuplicate.ts, 7, 11))
>b : Symbol(b, Decl(spreadDuplicate.ts, 3, 11))
let c2 = { a: 123, ...(t ? c : {}) }; // string | number
>c2 : Symbol(c2, Decl(spreadDuplicate.ts, 16, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 16, 10))
>t : Symbol(t, Decl(spreadDuplicate.ts, 7, 11))
>c : Symbol(c, Decl(spreadDuplicate.ts, 4, 11))
let d2 = { a: 123, ...(t ? d : {}) }; // string | number
>d2 : Symbol(d2, Decl(spreadDuplicate.ts, 17, 3))
>a : Symbol(a, Decl(spreadDuplicate.ts, 17, 10))
>t : Symbol(t, Decl(spreadDuplicate.ts, 7, 11))
>d : Symbol(d, Decl(spreadDuplicate.ts, 5, 11))

View file

@ -0,0 +1,94 @@
=== tests/cases/conformance/types/spread/spreadDuplicate.ts ===
// Repro from #44438
declare let a: { a: string };
>a : { a: string; }
>a : string
declare let b: { a?: string };
>b : { a?: string | undefined; }
>a : string | undefined
declare let c: { a: string | undefined };
>c : { a: string | undefined; }
>a : string | undefined
declare let d: { a?: string | undefined };
>d : { a?: string | undefined; }
>a : string | undefined
declare let t: boolean;
>t : boolean
let a1 = { a: 123, ...a }; // string (Error)
>a1 : { a: string; }
>{ a: 123, ...a } : { a: string; }
>a : number
>123 : 123
>a : { a: string; }
let b1 = { a: 123, ...b }; // string | number
>b1 : { a: string | number; }
>{ a: 123, ...b } : { a: string | number; }
>a : number
>123 : 123
>b : { a?: string | undefined; }
let c1 = { a: 123, ...c }; // string | undefined (Error)
>c1 : { a: string | undefined; }
>{ a: 123, ...c } : { a: string | undefined; }
>a : number
>123 : 123
>c : { a: string | undefined; }
let d1 = { a: 123, ...d }; // string | number
>d1 : { a: string | number; }
>{ a: 123, ...d } : { a: string | number; }
>a : number
>123 : 123
>d : { a?: string | undefined; }
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
>a2 : { a: string | number; }
>{ a: 123, ...(t ? a : {}) } : { a: string | number; }
>a : number
>123 : 123
>(t ? a : {}) : { a: string; } | {}
>t ? a : {} : { a: string; } | {}
>t : boolean
>a : { a: string; }
>{} : {}
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
>b2 : { a: string | number; }
>{ a: 123, ...(t ? b : {}) } : { a: string | number; }
>a : number
>123 : 123
>(t ? b : {}) : { a?: string | undefined; }
>t ? b : {} : { a?: string | undefined; }
>t : boolean
>b : { a?: string | undefined; }
>{} : {}
let c2 = { a: 123, ...(t ? c : {}) }; // string | number
>c2 : { a: string | number; }
>{ a: 123, ...(t ? c : {}) } : { a: string | number; }
>a : number
>123 : 123
>(t ? c : {}) : { a: string | undefined; } | {}
>t ? c : {} : { a: string | undefined; } | {}
>t : boolean
>c : { a: string | undefined; }
>{} : {}
let d2 = { a: 123, ...(t ? d : {}) }; // string | number
>d2 : { a: string | number; }
>{ a: 123, ...(t ? d : {}) } : { a: string | number; }
>a : number
>123 : 123
>(t ? d : {}) : { a?: string | undefined; }
>t ? d : {} : { a?: string | undefined; }
>t : boolean
>d : { a?: string | undefined; }
>{} : {}

View file

@ -0,0 +1,30 @@
tests/cases/conformance/types/spread/spreadDuplicateExact.ts(10,12): error TS2783: 'a' is specified more than once, so this usage will be overwritten.
tests/cases/conformance/types/spread/spreadDuplicateExact.ts(12,12): error TS2783: 'a' is specified more than once, so this usage will be overwritten.
==== tests/cases/conformance/types/spread/spreadDuplicateExact.ts (2 errors) ====
// Repro from #44438
declare let a: { a: string };
declare let b: { a?: string };
declare let c: { a: string | undefined };
declare let d: { a?: string | undefined };
declare let t: boolean;
let a1 = { a: 123, ...a }; // string (Error)
~~~~~~
!!! error TS2783: 'a' is specified more than once, so this usage will be overwritten.
!!! related TS2785 tests/cases/conformance/types/spread/spreadDuplicateExact.ts:10:20: This spread always overwrites this property.
let b1 = { a: 123, ...b }; // string | number
let c1 = { a: 123, ...c }; // string | undefined (Error)
~~~~~~
!!! error TS2783: 'a' is specified more than once, so this usage will be overwritten.
!!! related TS2785 tests/cases/conformance/types/spread/spreadDuplicateExact.ts:12:20: This spread always overwrites this property.
let d1 = { a: 123, ...d }; // string | number | undefined
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
let c2 = { a: 123, ...(t ? c : {}) }; // string | number | undefined
let d2 = { a: 123, ...(t ? d : {}) }; // string | number | undefined

View file

@ -0,0 +1,83 @@
//// [spreadDuplicateExact.ts]
// Repro from #44438
declare let a: { a: string };
declare let b: { a?: string };
declare let c: { a: string | undefined };
declare let d: { a?: string | undefined };
declare let t: boolean;
let a1 = { a: 123, ...a }; // string (Error)
let b1 = { a: 123, ...b }; // string | number
let c1 = { a: 123, ...c }; // string | undefined (Error)
let d1 = { a: 123, ...d }; // string | number | undefined
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
let c2 = { a: 123, ...(t ? c : {}) }; // string | number | undefined
let d2 = { a: 123, ...(t ? d : {}) }; // string | number | undefined
//// [spreadDuplicateExact.js]
"use strict";
// Repro from #44438
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
var a1 = __assign({ a: 123 }, a); // string (Error)
var b1 = __assign({ a: 123 }, b); // string | number
var c1 = __assign({ a: 123 }, c); // string | undefined (Error)
var d1 = __assign({ a: 123 }, d); // string | number | undefined
var a2 = __assign({ a: 123 }, (t ? a : {})); // string | number
var b2 = __assign({ a: 123 }, (t ? b : {})); // string | number
var c2 = __assign({ a: 123 }, (t ? c : {})); // string | number | undefined
var d2 = __assign({ a: 123 }, (t ? d : {})); // string | number | undefined
//// [spreadDuplicateExact.d.ts]
declare let a: {
a: string;
};
declare let b: {
a?: string;
};
declare let c: {
a: string | undefined;
};
declare let d: {
a?: string | undefined;
};
declare let t: boolean;
declare let a1: {
a: string;
};
declare let b1: {
a: string | number;
};
declare let c1: {
a: string | undefined;
};
declare let d1: {
a: string | number | undefined;
};
declare let a2: {
a: string | number;
};
declare let b2: {
a: string | number;
};
declare let c2: {
a: string | number | undefined;
};
declare let d2: {
a: string | number | undefined;
};

View file

@ -0,0 +1,66 @@
=== tests/cases/conformance/types/spread/spreadDuplicateExact.ts ===
// Repro from #44438
declare let a: { a: string };
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 2, 11))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 2, 16))
declare let b: { a?: string };
>b : Symbol(b, Decl(spreadDuplicateExact.ts, 3, 11))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 3, 16))
declare let c: { a: string | undefined };
>c : Symbol(c, Decl(spreadDuplicateExact.ts, 4, 11))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 4, 16))
declare let d: { a?: string | undefined };
>d : Symbol(d, Decl(spreadDuplicateExact.ts, 5, 11))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 5, 16))
declare let t: boolean;
>t : Symbol(t, Decl(spreadDuplicateExact.ts, 7, 11))
let a1 = { a: 123, ...a }; // string (Error)
>a1 : Symbol(a1, Decl(spreadDuplicateExact.ts, 9, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 9, 10))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 2, 11))
let b1 = { a: 123, ...b }; // string | number
>b1 : Symbol(b1, Decl(spreadDuplicateExact.ts, 10, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 10, 10))
>b : Symbol(b, Decl(spreadDuplicateExact.ts, 3, 11))
let c1 = { a: 123, ...c }; // string | undefined (Error)
>c1 : Symbol(c1, Decl(spreadDuplicateExact.ts, 11, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 11, 10))
>c : Symbol(c, Decl(spreadDuplicateExact.ts, 4, 11))
let d1 = { a: 123, ...d }; // string | number | undefined
>d1 : Symbol(d1, Decl(spreadDuplicateExact.ts, 12, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 12, 10))
>d : Symbol(d, Decl(spreadDuplicateExact.ts, 5, 11))
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
>a2 : Symbol(a2, Decl(spreadDuplicateExact.ts, 14, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 14, 10))
>t : Symbol(t, Decl(spreadDuplicateExact.ts, 7, 11))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 2, 11))
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
>b2 : Symbol(b2, Decl(spreadDuplicateExact.ts, 15, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 15, 10))
>t : Symbol(t, Decl(spreadDuplicateExact.ts, 7, 11))
>b : Symbol(b, Decl(spreadDuplicateExact.ts, 3, 11))
let c2 = { a: 123, ...(t ? c : {}) }; // string | number | undefined
>c2 : Symbol(c2, Decl(spreadDuplicateExact.ts, 16, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 16, 10))
>t : Symbol(t, Decl(spreadDuplicateExact.ts, 7, 11))
>c : Symbol(c, Decl(spreadDuplicateExact.ts, 4, 11))
let d2 = { a: 123, ...(t ? d : {}) }; // string | number | undefined
>d2 : Symbol(d2, Decl(spreadDuplicateExact.ts, 17, 3))
>a : Symbol(a, Decl(spreadDuplicateExact.ts, 17, 10))
>t : Symbol(t, Decl(spreadDuplicateExact.ts, 7, 11))
>d : Symbol(d, Decl(spreadDuplicateExact.ts, 5, 11))

View file

@ -0,0 +1,94 @@
=== tests/cases/conformance/types/spread/spreadDuplicateExact.ts ===
// Repro from #44438
declare let a: { a: string };
>a : { a: string; }
>a : string
declare let b: { a?: string };
>b : { a?: string; }
>a : string | undefined
declare let c: { a: string | undefined };
>c : { a: string | undefined; }
>a : string | undefined
declare let d: { a?: string | undefined };
>d : { a?: string | undefined; }
>a : string | undefined
declare let t: boolean;
>t : boolean
let a1 = { a: 123, ...a }; // string (Error)
>a1 : { a: string; }
>{ a: 123, ...a } : { a: string; }
>a : number
>123 : 123
>a : { a: string; }
let b1 = { a: 123, ...b }; // string | number
>b1 : { a: string | number; }
>{ a: 123, ...b } : { a: string | number; }
>a : number
>123 : 123
>b : { a?: string; }
let c1 = { a: 123, ...c }; // string | undefined (Error)
>c1 : { a: string | undefined; }
>{ a: 123, ...c } : { a: string | undefined; }
>a : number
>123 : 123
>c : { a: string | undefined; }
let d1 = { a: 123, ...d }; // string | number | undefined
>d1 : { a: string | number | undefined; }
>{ a: 123, ...d } : { a: string | number | undefined; }
>a : number
>123 : 123
>d : { a?: string | undefined; }
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
>a2 : { a: string | number; }
>{ a: 123, ...(t ? a : {}) } : { a: string | number; }
>a : number
>123 : 123
>(t ? a : {}) : { a: string; } | {}
>t ? a : {} : { a: string; } | {}
>t : boolean
>a : { a: string; }
>{} : {}
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
>b2 : { a: string | number; }
>{ a: 123, ...(t ? b : {}) } : { a: string | number; }
>a : number
>123 : 123
>(t ? b : {}) : { a?: string; }
>t ? b : {} : { a?: string; }
>t : boolean
>b : { a?: string; }
>{} : {}
let c2 = { a: 123, ...(t ? c : {}) }; // string | number | undefined
>c2 : { a: string | number | undefined; }
>{ a: 123, ...(t ? c : {}) } : { a: string | number | undefined; }
>a : number
>123 : 123
>(t ? c : {}) : { a: string | undefined; } | {}
>t ? c : {} : { a: string | undefined; } | {}
>t : boolean
>c : { a: string | undefined; }
>{} : {}
let d2 = { a: 123, ...(t ? d : {}) }; // string | number | undefined
>d2 : { a: string | number | undefined; }
>{ a: 123, ...(t ? d : {}) } : { a: string | number | undefined; }
>a : number
>123 : 123
>(t ? d : {}) : { a?: string | undefined; }
>t ? d : {} : { a?: string | undefined; }
>t : boolean
>d : { a?: string | undefined; }
>{} : {}

View file

@ -1,10 +1,11 @@
tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(3,17): error TS2783: 'b' is specified more than once, so this usage will be overwritten.
tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(9,14): error TS2783: 'x' is specified more than once, so this usage will be overwritten.
tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(15,14): error TS2783: 'x' is specified more than once, so this usage will be overwritten.
tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(24,14): error TS2783: 'command' is specified more than once, so this usage will be overwritten.
tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(28,14): error TS2783: 'a' is specified more than once, so this usage will be overwritten.
==== tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts (4 errors) ====
==== tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts (5 errors) ====
declare var ab: { a: number, b: number };
declare var abq: { a: number, b?: number };
var unused1 = { b: 1, ...ab } // error
@ -17,6 +18,9 @@ tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(28,14): e
var unused5 = { ...abq, b: 1 } // ok
function g(obj: { x: number | undefined }) {
return { x: 1, ...obj }; // ok, obj might have x: undefined
~~~~
!!! error TS2783: 'x' is specified more than once, so this usage will be overwritten.
!!! related TS2785 tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts:9:20: This spread always overwrites this property.
}
function f(obj: { x: number } | undefined) {
return { x: 1, ...obj }; // ok, obj might be undefined

View file

@ -0,0 +1,21 @@
// @strict: true
// @declaration: true
// Repro from #44438
declare let a: { a: string };
declare let b: { a?: string };
declare let c: { a: string | undefined };
declare let d: { a?: string | undefined };
declare let t: boolean;
let a1 = { a: 123, ...a }; // string (Error)
let b1 = { a: 123, ...b }; // string | number
let c1 = { a: 123, ...c }; // string | undefined (Error)
let d1 = { a: 123, ...d }; // string | number
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
let c2 = { a: 123, ...(t ? c : {}) }; // string | number
let d2 = { a: 123, ...(t ? d : {}) }; // string | number

View file

@ -0,0 +1,22 @@
// @strict: true
// @exactOptionalPropertyTypes: true
// @declaration: true
// Repro from #44438
declare let a: { a: string };
declare let b: { a?: string };
declare let c: { a: string | undefined };
declare let d: { a?: string | undefined };
declare let t: boolean;
let a1 = { a: 123, ...a }; // string (Error)
let b1 = { a: 123, ...b }; // string | number
let c1 = { a: 123, ...c }; // string | undefined (Error)
let d1 = { a: 123, ...d }; // string | number | undefined
let a2 = { a: 123, ...(t ? a : {}) }; // string | number
let b2 = { a: 123, ...(t ? b : {}) }; // string | number
let c2 = { a: 123, ...(t ? c : {}) }; // string | number | undefined
let d2 = { a: 123, ...(t ? d : {}) }; // string | number | undefined