From 98a688f20defba33036348dfddb383b3fe4f5e70 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders <293473+sandersn@users.noreply.github.com> Date: Thu, 30 Aug 2018 15:30:35 -0700 Subject: [PATCH] No excess property error for spread properties That is, properties in an object literal type that came from a spread assignment never cause an excess property error. --- src/compiler/checker.ts | 6 +- .../reference/objectSpreadNegative.errors.txt | 27 +-------- .../reference/objectSpreadNegative.js | 16 ------ .../reference/objectSpreadNegative.symbols | 47 --------------- .../reference/objectSpreadNegative.types | 57 ------------------- .../reference/spreadExcessProperty.js | 20 +++++++ .../reference/spreadExcessProperty.symbols | 17 ++++++ .../reference/spreadExcessProperty.types | 21 +++++++ .../types/spread/objectSpreadNegative.ts | 10 ---- .../types/spread/spreadExcessProperty.ts | 3 + 10 files changed, 67 insertions(+), 157 deletions(-) create mode 100644 tests/baselines/reference/spreadExcessProperty.js create mode 100644 tests/baselines/reference/spreadExcessProperty.symbols create mode 100644 tests/baselines/reference/spreadExcessProperty.types create mode 100644 tests/cases/conformance/types/spread/spreadExcessProperty.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 4ebb8a23de9e0..2b367794ff2fe 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -11324,7 +11324,7 @@ namespace ts { return hasExcessProperties(source, discriminant, /*discriminant*/ undefined, reportErrors); } for (const prop of getPropertiesOfObjectType(source)) { - if (!isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { + if (!isPropertyFromSpread(prop, source.symbol) && !isKnownProperty(target, prop.escapedName, isComparingJsxAttributes)) { if (reportErrors) { // We know *exactly* where things went wrong when comparing the types. // Use this property as the error node as this will be more helpful in @@ -11368,6 +11368,10 @@ namespace ts { return false; } + function isPropertyFromSpread(prop: Symbol, container: Symbol) { + return prop.valueDeclaration && container.valueDeclaration && prop.valueDeclaration.parent !== container.valueDeclaration; + } + function eachTypeRelatedToSomeType(source: UnionOrIntersectionType, target: UnionOrIntersectionType): Ternary { let result = Ternary.True; const sourceTypes = source.types; diff --git a/tests/baselines/reference/objectSpreadNegative.errors.txt b/tests/baselines/reference/objectSpreadNegative.errors.txt index 92225755c7191..1af059068733e 100644 --- a/tests/baselines/reference/objectSpreadNegative.errors.txt +++ b/tests/baselines/reference/objectSpreadNegative.errors.txt @@ -18,15 +18,9 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(53,9): error TS2339 tests/cases/conformance/types/spread/objectSpreadNegative.ts(58,11): error TS2339: Property 'a' does not exist on type '{}'. tests/cases/conformance/types/spread/objectSpreadNegative.ts(62,14): error TS2698: Spread types may only be created from object types. tests/cases/conformance/types/spread/objectSpreadNegative.ts(65,14): error TS2698: Spread types may only be created from object types. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(79,37): error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'. - Object literal may only specify known properties, and 'extra' does not exist in type 'A'. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(82,7): error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'. - Object literal may only specify known properties, and 'extra' does not exist in type 'A'. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(84,7): error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'. - Object literal may only specify known properties, and 'extra' does not exist in type 'A'. -==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (20 errors) ==== +==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (17 errors) ==== let o = { a: 1, b: 'no' } /// private propagates @@ -138,23 +132,4 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(84,7): error TS2322 f({ a: 1 }, { a: 'mismatch' }) let overwriteId: { id: string, a: number, c: number, d: string } = f({ a: 1, id: true }, { c: 1, d: 'no' }) - - // excess property checks - type A = { a: string, b: string }; - type Extra = { a: string, b: string, extra: string }; - const extra1: A = { a: "a", b: "b", extra: "extra" }; - ~~~~~~~~~~~~~~ -!!! error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'. -!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'A'. - const extra2 = { a: "a", b: "b", extra: "extra" }; - const a1: A = { ...extra1 }; // error spans should be here - const a2: A = { ...extra2 }; // not on the symbol declarations above - ~~ -!!! error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'. -!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'A'. - const extra3: Extra = { a: "a", b: "b", extra: "extra" }; - const a3: A = { ...extra3 }; // same here - ~~ -!!! error TS2322: Type '{ a: string; b: string; extra: string; }' is not assignable to type 'A'. -!!! error TS2322: Object literal may only specify known properties, and 'extra' does not exist in type 'A'. \ No newline at end of file diff --git a/tests/baselines/reference/objectSpreadNegative.js b/tests/baselines/reference/objectSpreadNegative.js index 63ae891438514..35d8cdf9830fc 100644 --- a/tests/baselines/reference/objectSpreadNegative.js +++ b/tests/baselines/reference/objectSpreadNegative.js @@ -73,16 +73,6 @@ let overlapConflict: { id:string, a: string } = f({ a: 1 }, { a: 'mismatch' }) let overwriteId: { id: string, a: number, c: number, d: string } = f({ a: 1, id: true }, { c: 1, d: 'no' }) - -// excess property checks -type A = { a: string, b: string }; -type Extra = { a: string, b: string, extra: string }; -const extra1: A = { a: "a", b: "b", extra: "extra" }; -const extra2 = { a: "a", b: "b", extra: "extra" }; -const a1: A = { ...extra1 }; // error spans should be here -const a2: A = { ...extra2 }; // not on the symbol declarations above -const extra3: Extra = { a: "a", b: "b", extra: "extra" }; -const a3: A = { ...extra3 }; // same here //// [objectSpreadNegative.js] @@ -167,9 +157,3 @@ var exclusive = f({ a: 1, b: 'yes' }, { c: 'no', d: false }); var overlap = f({ a: 1 }, { a: 2, b: 'extra' }); var overlapConflict = f({ a: 1 }, { a: 'mismatch' }); var overwriteId = f({ a: 1, id: true }, { c: 1, d: 'no' }); -var extra1 = { a: "a", b: "b", extra: "extra" }; -var extra2 = { a: "a", b: "b", extra: "extra" }; -var a1 = __assign({}, extra1); // error spans should be here -var a2 = __assign({}, extra2); // not on the symbol declarations above -var extra3 = { a: "a", b: "b", extra: "extra" }; -var a3 = __assign({}, extra3); // same here diff --git a/tests/baselines/reference/objectSpreadNegative.symbols b/tests/baselines/reference/objectSpreadNegative.symbols index 21af20facbb8e..4bff16d9be602 100644 --- a/tests/baselines/reference/objectSpreadNegative.symbols +++ b/tests/baselines/reference/objectSpreadNegative.symbols @@ -243,50 +243,3 @@ let overwriteId: { id: string, a: number, c: number, d: string } = >c : Symbol(c, Decl(objectSpreadNegative.ts, 73, 27)) >d : Symbol(d, Decl(objectSpreadNegative.ts, 73, 33)) -// excess property checks -type A = { a: string, b: string }; ->A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44)) ->a : Symbol(a, Decl(objectSpreadNegative.ts, 76, 10)) ->b : Symbol(b, Decl(objectSpreadNegative.ts, 76, 21)) - -type Extra = { a: string, b: string, extra: string }; ->Extra : Symbol(Extra, Decl(objectSpreadNegative.ts, 76, 34)) ->a : Symbol(a, Decl(objectSpreadNegative.ts, 77, 14)) ->b : Symbol(b, Decl(objectSpreadNegative.ts, 77, 25)) ->extra : Symbol(extra, Decl(objectSpreadNegative.ts, 77, 36)) - -const extra1: A = { a: "a", b: "b", extra: "extra" }; ->extra1 : Symbol(extra1, Decl(objectSpreadNegative.ts, 78, 5)) ->A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44)) ->a : Symbol(a, Decl(objectSpreadNegative.ts, 78, 19)) ->b : Symbol(b, Decl(objectSpreadNegative.ts, 78, 27)) ->extra : Symbol(extra, Decl(objectSpreadNegative.ts, 78, 35)) - -const extra2 = { a: "a", b: "b", extra: "extra" }; ->extra2 : Symbol(extra2, Decl(objectSpreadNegative.ts, 79, 5)) ->a : Symbol(a, Decl(objectSpreadNegative.ts, 79, 16)) ->b : Symbol(b, Decl(objectSpreadNegative.ts, 79, 24)) ->extra : Symbol(extra, Decl(objectSpreadNegative.ts, 79, 32)) - -const a1: A = { ...extra1 }; // error spans should be here ->a1 : Symbol(a1, Decl(objectSpreadNegative.ts, 80, 5)) ->A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44)) ->extra1 : Symbol(extra1, Decl(objectSpreadNegative.ts, 78, 5)) - -const a2: A = { ...extra2 }; // not on the symbol declarations above ->a2 : Symbol(a2, Decl(objectSpreadNegative.ts, 81, 5)) ->A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44)) ->extra2 : Symbol(extra2, Decl(objectSpreadNegative.ts, 79, 5)) - -const extra3: Extra = { a: "a", b: "b", extra: "extra" }; ->extra3 : Symbol(extra3, Decl(objectSpreadNegative.ts, 82, 5)) ->Extra : Symbol(Extra, Decl(objectSpreadNegative.ts, 76, 34)) ->a : Symbol(a, Decl(objectSpreadNegative.ts, 82, 23)) ->b : Symbol(b, Decl(objectSpreadNegative.ts, 82, 31)) ->extra : Symbol(extra, Decl(objectSpreadNegative.ts, 82, 39)) - -const a3: A = { ...extra3 }; // same here ->a3 : Symbol(a3, Decl(objectSpreadNegative.ts, 83, 5)) ->A : Symbol(A, Decl(objectSpreadNegative.ts, 73, 44)) ->extra3 : Symbol(extra3, Decl(objectSpreadNegative.ts, 82, 5)) - diff --git a/tests/baselines/reference/objectSpreadNegative.types b/tests/baselines/reference/objectSpreadNegative.types index 0d0eab1c35d00..26fd5642d2301 100644 --- a/tests/baselines/reference/objectSpreadNegative.types +++ b/tests/baselines/reference/objectSpreadNegative.types @@ -325,60 +325,3 @@ let overwriteId: { id: string, a: number, c: number, d: string } = >d : string >'no' : "no" -// excess property checks -type A = { a: string, b: string }; ->A : A ->a : string ->b : string - -type Extra = { a: string, b: string, extra: string }; ->Extra : Extra ->a : string ->b : string ->extra : string - -const extra1: A = { a: "a", b: "b", extra: "extra" }; ->extra1 : A ->{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; } ->a : string ->"a" : "a" ->b : string ->"b" : "b" ->extra : string ->"extra" : "extra" - -const extra2 = { a: "a", b: "b", extra: "extra" }; ->extra2 : { a: string; b: string; extra: string; } ->{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; } ->a : string ->"a" : "a" ->b : string ->"b" : "b" ->extra : string ->"extra" : "extra" - -const a1: A = { ...extra1 }; // error spans should be here ->a1 : A ->{ ...extra1 } : { a: string; b: string; } ->extra1 : A - -const a2: A = { ...extra2 }; // not on the symbol declarations above ->a2 : A ->{ ...extra2 } : { a: string; b: string; extra: string; } ->extra2 : { a: string; b: string; extra: string; } - -const extra3: Extra = { a: "a", b: "b", extra: "extra" }; ->extra3 : Extra ->{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; } ->a : string ->"a" : "a" ->b : string ->"b" : "b" ->extra : string ->"extra" : "extra" - -const a3: A = { ...extra3 }; // same here ->a3 : A ->{ ...extra3 } : { a: string; b: string; extra: string; } ->extra3 : Extra - diff --git a/tests/baselines/reference/spreadExcessProperty.js b/tests/baselines/reference/spreadExcessProperty.js new file mode 100644 index 0000000000000..c5bf845224cb5 --- /dev/null +++ b/tests/baselines/reference/spreadExcessProperty.js @@ -0,0 +1,20 @@ +//// [spreadExcessProperty.ts] +type A = { a: string, b: string }; +const extra1 = { a: "a", b: "b", extra: "extra" }; +const a1: A = { ...extra1 }; // spread should not give excess property errors + + +//// [spreadExcessProperty.js] +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 extra1 = { a: "a", b: "b", extra: "extra" }; +var a1 = __assign({}, extra1); // spread should not give excess property errors diff --git a/tests/baselines/reference/spreadExcessProperty.symbols b/tests/baselines/reference/spreadExcessProperty.symbols new file mode 100644 index 0000000000000..bc4ef20664398 --- /dev/null +++ b/tests/baselines/reference/spreadExcessProperty.symbols @@ -0,0 +1,17 @@ +=== tests/cases/conformance/types/spread/spreadExcessProperty.ts === +type A = { a: string, b: string }; +>A : Symbol(A, Decl(spreadExcessProperty.ts, 0, 0)) +>a : Symbol(a, Decl(spreadExcessProperty.ts, 0, 10)) +>b : Symbol(b, Decl(spreadExcessProperty.ts, 0, 21)) + +const extra1 = { a: "a", b: "b", extra: "extra" }; +>extra1 : Symbol(extra1, Decl(spreadExcessProperty.ts, 1, 5)) +>a : Symbol(a, Decl(spreadExcessProperty.ts, 1, 16)) +>b : Symbol(b, Decl(spreadExcessProperty.ts, 1, 24)) +>extra : Symbol(extra, Decl(spreadExcessProperty.ts, 1, 32)) + +const a1: A = { ...extra1 }; // spread should not give excess property errors +>a1 : Symbol(a1, Decl(spreadExcessProperty.ts, 2, 5)) +>A : Symbol(A, Decl(spreadExcessProperty.ts, 0, 0)) +>extra1 : Symbol(extra1, Decl(spreadExcessProperty.ts, 1, 5)) + diff --git a/tests/baselines/reference/spreadExcessProperty.types b/tests/baselines/reference/spreadExcessProperty.types new file mode 100644 index 0000000000000..015c7cc85aad6 --- /dev/null +++ b/tests/baselines/reference/spreadExcessProperty.types @@ -0,0 +1,21 @@ +=== tests/cases/conformance/types/spread/spreadExcessProperty.ts === +type A = { a: string, b: string }; +>A : A +>a : string +>b : string + +const extra1 = { a: "a", b: "b", extra: "extra" }; +>extra1 : { a: string; b: string; extra: string; } +>{ a: "a", b: "b", extra: "extra" } : { a: string; b: string; extra: string; } +>a : string +>"a" : "a" +>b : string +>"b" : "b" +>extra : string +>"extra" : "extra" + +const a1: A = { ...extra1 }; // spread should not give excess property errors +>a1 : A +>{ ...extra1 } : { a: string; b: string; extra: string; } +>extra1 : { a: string; b: string; extra: string; } + diff --git a/tests/cases/conformance/types/spread/objectSpreadNegative.ts b/tests/cases/conformance/types/spread/objectSpreadNegative.ts index b6e7c5b88c98d..789016762da37 100644 --- a/tests/cases/conformance/types/spread/objectSpreadNegative.ts +++ b/tests/cases/conformance/types/spread/objectSpreadNegative.ts @@ -73,13 +73,3 @@ let overlapConflict: { id:string, a: string } = f({ a: 1 }, { a: 'mismatch' }) let overwriteId: { id: string, a: number, c: number, d: string } = f({ a: 1, id: true }, { c: 1, d: 'no' }) - -// excess property checks -type A = { a: string, b: string }; -type Extra = { a: string, b: string, extra: string }; -const extra1: A = { a: "a", b: "b", extra: "extra" }; -const extra2 = { a: "a", b: "b", extra: "extra" }; -const a1: A = { ...extra1 }; // error spans should be here -const a2: A = { ...extra2 }; // not on the symbol declarations above -const extra3: Extra = { a: "a", b: "b", extra: "extra" }; -const a3: A = { ...extra3 }; // same here diff --git a/tests/cases/conformance/types/spread/spreadExcessProperty.ts b/tests/cases/conformance/types/spread/spreadExcessProperty.ts new file mode 100644 index 0000000000000..b5b304e2ff7e8 --- /dev/null +++ b/tests/cases/conformance/types/spread/spreadExcessProperty.ts @@ -0,0 +1,3 @@ +type A = { a: string, b: string }; +const extra1 = { a: "a", b: "b", extra: "extra" }; +const a1: A = { ...extra1 }; // spread should not give excess property errors