Skip to content

Commit b687caf

Browse files
authored
No excess property error for spread properties (microsoft#26798)
That is, properties in an object literal type that came from a spread assignment never cause an excess property error.
1 parent cd37e41 commit b687caf

10 files changed

+67
-157
lines changed

src/compiler/checker.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11328,7 +11328,7 @@ namespace ts {
1132811328
return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors);
1132911329
}
1133011330
for (const prop of getPropertiesOfObjectType(source)) {
11331-
if (!isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
11331+
if (!isPropertyFromSpread(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) {
1133211332
if (reportErrors) {
1133311333
// We know *exactly* where things went wrong when comparing the types.
1133411334
// Use this property as the error node as this will be more helpful in
@@ -11372,6 +11372,10 @@ namespace ts {
1137211372
return false;
1137311373
}
1137411374

11375+
function isPropertyFromSpread(prop: Symbol, container: Symbol) {
11376+
return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent !== container.valueDeclaration;
11377+
}
11378+
1137511379
function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary {
1137611380
let result = Ternary.True;
1137711381
const sourceTypes = source.types;

tests/baselines/reference/objectSpreadNegative.errors.txt

Lines changed: 1 addition & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,9 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(53,9): error TS2339
1818
tests/cases/conformance/types/spread/objectSpreadNegative.ts(58,11): error TS2339: Property 'a' does not exist on type '{}'.
1919
tests/cases/conformance/types/spread/objectSpreadNegative.ts(62,14): error TS2698: Spread types may only be created from object types.
2020
tests/cases/conformance/types/spread/objectSpreadNegative.ts(65,14): error TS2698: Spread types may only be created from object types.
21-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(79,37): error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'.
22-
Object literal may only specify known properties, and 'extra' does not exist in type 'A'.
23-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(82,7): error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'.
24-
Object literal may only specify known properties, and 'extra' does not exist in type 'A'.
25-
tests/cases/conformance/types/spread/objectSpreadNegative.ts(84,7): error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'.
26-
Object literal may only specify known properties, and 'extra' does not exist in type 'A'.
2721

2822

29-
==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (20 errors) ====
23+
==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (17 errors) ====
3024
let o = { a: 1, b: 'no' }
3125

3226
/// private propagates
@@ -138,23 +132,4 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(84,7): error TS2322
138132
f({ a: 1 }, { a: 'mismatch' })
139133
let overwriteId: { id: string, a: number, c: number, d: string } =
140134
f({ a: 1, id: true }, { c: 1, d: 'no' })
141-
142-
// excess property checks
143-
type A = { a: string, b: string };
144-
type Extra = { a: string, b: string, extra: string };
145-
const extra1: A = { a: "a", b: "b", extra: "extra" };
146-
~~~~~~~~~~~~~~
147-
!!! error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'.
148-
!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'A'.
149-
const extra2 = { a: "a", b: "b", extra: "extra" };
150-
const a1: A = { ...extra1 }; // error spans should be here
151-
const a2: A = { ...extra2 }; // not on the symbol declarations above
152-
~~
153-
!!! error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'.
154-
!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'A'.
155-
const extra3: Extra = { a: "a", b: "b", extra: "extra" };
156-
const a3: A = { ...extra3 }; // same here
157-
~~
158-
!!! error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'.
159-
!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'A'.
160135

tests/baselines/reference/objectSpreadNegative.js

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -73,16 +73,6 @@ let overlapConflict: { id:string, a: string } =
7373
f({ a: 1 }, { a: 'mismatch' })
7474
let overwriteId: { id: string, a: number, c: number, d: string } =
7575
f({ a: 1, id: true }, { c: 1, d: 'no' })
76-
77-
// excess property checks
78-
type A = { a: string, b: string };
79-
type Extra = { a: string, b: string, extra: string };
80-
const extra1: A = { a: "a", b: "b", extra: "extra" };
81-
const extra2 = { a: "a", b: "b", extra: "extra" };
82-
const a1: A = { ...extra1 }; // error spans should be here
83-
const a2: A = { ...extra2 }; // not on the symbol declarations above
84-
const extra3: Extra = { a: "a", b: "b", extra: "extra" };
85-
const a3: A = { ...extra3 }; // same here
8676

8777

8878
//// [objectSpreadNegative.js]
@@ -167,9 +157,3 @@ var exclusive = f({ a: 1, b: 'yes' }, { c: 'no', d: false });
167157
var overlap = f({ a: 1 }, { a: 2, b: 'extra' });
168158
var overlapConflict = f({ a: 1 }, { a: 'mismatch' });
169159
var overwriteId = f({ a: 1, id: true }, { c: 1, d: 'no' });
170-
var extra1 = { a: "a", b: "b", extra: "extra" };
171-
var extra2 = { a: "a", b: "b", extra: "extra" };
172-
var a1 = __assign({}, extra1); // error spans should be here
173-
var a2 = __assign({}, extra2); // not on the symbol declarations above
174-
var extra3 = { a: "a", b: "b", extra: "extra" };
175-
var a3 = __assign({}, extra3); // same here

tests/baselines/reference/objectSpreadNegative.symbols

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -243,50 +243,3 @@ let overwriteId: { id: string, a: number, c: number, d: string } =
243243
>c : Symbol(c, Decl(objectSpreadNegative.ts, 73, 27))
244244
>d : Symbol(d, Decl(objectSpreadNegative.ts, 73, 33))
245245

246-
// excess property checks
247-
type A = { a: string, b: string };
248-
>A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44))
249-
>a : Symbol(a, Decl(objectSpreadNegative.ts, 76, 10))
250-
>b : Symbol(b, Decl(objectSpreadNegative.ts, 76, 21))
251-
252-
type Extra = { a: string, b: string, extra: string };
253-
>Extra : Symbol(Extra, Decl(objectSpreadNegative.ts, 76, 34))
254-
>a : Symbol(a, Decl(objectSpreadNegative.ts, 77, 14))
255-
>b : Symbol(b, Decl(objectSpreadNegative.ts, 77, 25))
256-
>extra : Symbol(extra, Decl(objectSpreadNegative.ts, 77, 36))
257-
258-
const extra1: A = { a: "a", b: "b", extra: "extra" };
259-
>extra1 : Symbol(extra1, Decl(objectSpreadNegative.ts, 78, 5))
260-
>A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44))
261-
>a : Symbol(a, Decl(objectSpreadNegative.ts, 78, 19))
262-
>b : Symbol(b, Decl(objectSpreadNegative.ts, 78, 27))
263-
>extra : Symbol(extra, Decl(objectSpreadNegative.ts, 78, 35))
264-
265-
const extra2 = { a: "a", b: "b", extra: "extra" };
266-
>extra2 : Symbol(extra2, Decl(objectSpreadNegative.ts, 79, 5))
267-
>a : Symbol(a, Decl(objectSpreadNegative.ts, 79, 16))
268-
>b : Symbol(b, Decl(objectSpreadNegative.ts, 79, 24))
269-
>extra : Symbol(extra, Decl(objectSpreadNegative.ts, 79, 32))
270-
271-
const a1: A = { ...extra1 }; // error spans should be here
272-
>a1 : Symbol(a1, Decl(objectSpreadNegative.ts, 80, 5))
273-
>A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44))
274-
>extra1 : Symbol(extra1, Decl(objectSpreadNegative.ts, 78, 5))
275-
276-
const a2: A = { ...extra2 }; // not on the symbol declarations above
277-
>a2 : Symbol(a2, Decl(objectSpreadNegative.ts, 81, 5))
278-
>A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44))
279-
>extra2 : Symbol(extra2, Decl(objectSpreadNegative.ts, 79, 5))
280-
281-
const extra3: Extra = { a: "a", b: "b", extra: "extra" };
282-
>extra3 : Symbol(extra3, Decl(objectSpreadNegative.ts, 82, 5))
283-
>Extra : Symbol(Extra, Decl(objectSpreadNegative.ts, 76, 34))
284-
>a : Symbol(a, Decl(objectSpreadNegative.ts, 82, 23))
285-
>b : Symbol(b, Decl(objectSpreadNegative.ts, 82, 31))
286-
>extra : Symbol(extra, Decl(objectSpreadNegative.ts, 82, 39))
287-
288-
const a3: A = { ...extra3 }; // same here
289-
>a3 : Symbol(a3, Decl(objectSpreadNegative.ts, 83, 5))
290-
>A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44))
291-
>extra3 : Symbol(extra3, Decl(objectSpreadNegative.ts, 82, 5))
292-

tests/baselines/reference/objectSpreadNegative.types

Lines changed: 0 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -325,60 +325,3 @@ let overwriteId: { id: string, a: number, c: number, d: string } =
325325
>d : string
326326
>'no' : "no"
327327

328-
// excess property checks
329-
type A = { a: string, b: string };
330-
>A : A
331-
>a : string
332-
>b : string
333-
334-
type Extra = { a: string, b: string, extra: string };
335-
>Extra : Extra
336-
>a : string
337-
>b : string
338-
>extra : string
339-
340-
const extra1: A = { a: "a", b: "b", extra: "extra" };
341-
>extra1 : A
342-
>{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; }
343-
>a : string
344-
>"a" : "a"
345-
>b : string
346-
>"b" : "b"
347-
>extra : string
348-
>"extra" : "extra"
349-
350-
const extra2 = { a: "a", b: "b", extra: "extra" };
351-
>extra2 : { a: string; b: string; extra: string; }
352-
>{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; }
353-
>a : string
354-
>"a" : "a"
355-
>b : string
356-
>"b" : "b"
357-
>extra : string
358-
>"extra" : "extra"
359-
360-
const a1: A = { ...extra1 }; // error spans should be here
361-
>a1 : A
362-
>{ ...extra1 } : { a: string; b: string; }
363-
>extra1 : A
364-
365-
const a2: A = { ...extra2 }; // not on the symbol declarations above
366-
>a2 : A
367-
>{ ...extra2 } : { a: string; b: string; extra: string; }
368-
>extra2 : { a: string; b: string; extra: string; }
369-
370-
const extra3: Extra = { a: "a", b: "b", extra: "extra" };
371-
>extra3 : Extra
372-
>{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; }
373-
>a : string
374-
>"a" : "a"
375-
>b : string
376-
>"b" : "b"
377-
>extra : string
378-
>"extra" : "extra"
379-
380-
const a3: A = { ...extra3 }; // same here
381-
>a3 : A
382-
>{ ...extra3 } : { a: string; b: string; extra: string; }
383-
>extra3 : Extra
384-
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//// [spreadExcessProperty.ts]
2+
type A = { a: string, b: string };
3+
const extra1 = { a: "a", b: "b", extra: "extra" };
4+
const a1: A = { ...extra1 }; // spread should not give excess property errors
5+
6+
7+
//// [spreadExcessProperty.js]
8+
var __assign = (this && this.__assign) || function () {
9+
__assign = Object.assign || function(t) {
10+
for (var s, i = 1, n = arguments.length; i < n; i++) {
11+
s = arguments[i];
12+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
13+
t[p] = s[p];
14+
}
15+
return t;
16+
};
17+
return __assign.apply(this, arguments);
18+
};
19+
var extra1 = { a: "a", b: "b", extra: "extra" };
20+
var a1 = __assign({}, extra1); // spread should not give excess property errors
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
=== tests/cases/conformance/types/spread/spreadExcessProperty.ts ===
2+
type A = { a: string, b: string };
3+
>A : Symbol(A, Decl(spreadExcessProperty.ts, 0, 0))
4+
>a : Symbol(a, Decl(spreadExcessProperty.ts, 0, 10))
5+
>b : Symbol(b, Decl(spreadExcessProperty.ts, 0, 21))
6+
7+
const extra1 = { a: "a", b: "b", extra: "extra" };
8+
>extra1 : Symbol(extra1, Decl(spreadExcessProperty.ts, 1, 5))
9+
>a : Symbol(a, Decl(spreadExcessProperty.ts, 1, 16))
10+
>b : Symbol(b, Decl(spreadExcessProperty.ts, 1, 24))
11+
>extra : Symbol(extra, Decl(spreadExcessProperty.ts, 1, 32))
12+
13+
const a1: A = { ...extra1 }; // spread should not give excess property errors
14+
>a1 : Symbol(a1, Decl(spreadExcessProperty.ts, 2, 5))
15+
>A : Symbol(A, Decl(spreadExcessProperty.ts, 0, 0))
16+
>extra1 : Symbol(extra1, Decl(spreadExcessProperty.ts, 1, 5))
17+
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
=== tests/cases/conformance/types/spread/spreadExcessProperty.ts ===
2+
type A = { a: string, b: string };
3+
>A : A
4+
>a : string
5+
>b : string
6+
7+
const extra1 = { a: "a", b: "b", extra: "extra" };
8+
>extra1 : { a: string; b: string; extra: string; }
9+
>{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; }
10+
>a : string
11+
>"a" : "a"
12+
>b : string
13+
>"b" : "b"
14+
>extra : string
15+
>"extra" : "extra"
16+
17+
const a1: A = { ...extra1 }; // spread should not give excess property errors
18+
>a1 : A
19+
>{ ...extra1 } : { a: string; b: string; extra: string; }
20+
>extra1 : { a: string; b: string; extra: string; }
21+

tests/cases/conformance/types/spread/objectSpreadNegative.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,13 +73,3 @@ let overlapConflict: { id:string, a: string } =
7373
f({ a: 1 }, { a: 'mismatch' })
7474
let overwriteId: { id: string, a: number, c: number, d: string } =
7575
f({ a: 1, id: true }, { c: 1, d: 'no' })
76-
77-
// excess property checks
78-
type A = { a: string, b: string };
79-
type Extra = { a: string, b: string, extra: string };
80-
const extra1: A = { a: "a", b: "b", extra: "extra" };
81-
const extra2 = { a: "a", b: "b", extra: "extra" };
82-
const a1: A = { ...extra1 }; // error spans should be here
83-
const a2: A = { ...extra2 }; // not on the symbol declarations above
84-
const extra3: Extra = { a: "a", b: "b", extra: "extra" };
85-
const a3: A = { ...extra3 }; // same here
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
type A = { a: string, b: string };
2+
const extra1 = { a: "a", b: "b", extra: "extra" };
3+
const a1: A = { ...extra1 }; // spread should not give excess property errors

0 commit comments

Comments
 (0)