From 08d7e182cd40e152a3ab8291a10fcb40913f0caa Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 17 Oct 2017 09:56:04 -0700 Subject: [PATCH 1/2] Mark fresh spread objects w/ContainsObjectLiteral --- src/compiler/checker.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 69a46ed6cb236..f30d956e311df 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7971,7 +7971,7 @@ namespace ts { const spread = createAnonymousType(undefined, members, emptyArray, emptyArray, stringIndexInfo, numberIndexInfo); spread.flags |= propagatedFlags; - spread.flags |= TypeFlags.FreshLiteral; + spread.flags |= TypeFlags.FreshLiteral | TypeFlags.ContainsObjectLiteral; (spread as ObjectType).objectFlags |= ObjectFlags.ObjectLiteral; spread.symbol = symbol; return spread; From e58aa100687e3c1e92b04d927cd78d64f233ea3e Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Tue, 17 Oct 2017 09:56:28 -0700 Subject: [PATCH 2/2] Test excess property checks of spreads of unions. --- .../excessPropertyCheckWithUnions.errors.txt | 6 +++++ .../excessPropertyCheckWithUnions.js | 17 ++++++++++++ .../excessPropertyCheckWithUnions.symbols | 25 +++++++++++++++++ .../excessPropertyCheckWithUnions.types | 27 +++++++++++++++++++ .../reference/spreadInvalidArgumentType.types | 2 +- .../compiler/excessPropertyCheckWithUnions.ts | 7 +++++ 6 files changed, 83 insertions(+), 1 deletion(-) diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt index 3b7e5a787d2aa..9fbbb00160540 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.errors.txt @@ -96,4 +96,10 @@ tests/cases/compiler/excessPropertyCheckWithUnions.ts(40,1): error TS2322: Type // these two are not reported because there are two discriminant properties over = { a: 1, b: 1, first: "ok", second: "error" } over = { a: 1, b: 1, first: "ok", third: "error" } + + // Freshness disappears after spreading a union + declare let t0: { a: any, b: any } | { d: any, e: any } + declare let t1: { a: any, b: any, c: any } | { c: any, d: any, e: any } + let t2 = { ...t1 } + t0 = t2 \ No newline at end of file diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.js b/tests/baselines/reference/excessPropertyCheckWithUnions.js index c6da660b52dde..c6b45123cce76 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.js +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.js @@ -49,9 +49,24 @@ let over: Overlapping // these two are not reported because there are two discriminant properties over = { a: 1, b: 1, first: "ok", second: "error" } over = { a: 1, b: 1, first: "ok", third: "error" } + +// Freshness disappears after spreading a union +declare let t0: { a: any, b: any } | { d: any, e: any } +declare let t1: { a: any, b: any, c: any } | { c: any, d: any, e: any } +let t2 = { ...t1 } +t0 = t2 //// [excessPropertyCheckWithUnions.js] +"use strict"; +var __assign = (this && this.__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; +}; var wrong = { tag: "T", a1: "extra" }; wrong = { tag: "A", d20: 12 }; wrong = { tag: "D" }; @@ -72,3 +87,5 @@ var over; // these two are not reported because there are two discriminant properties over = { a: 1, b: 1, first: "ok", second: "error" }; over = { a: 1, b: 1, first: "ok", third: "error" }; +var t2 = __assign({}, t1); +t0 = t2; diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols index 332166e396c8f..7778c6bf21699 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.symbols +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.symbols @@ -142,3 +142,28 @@ over = { a: 1, b: 1, first: "ok", third: "error" } >first : Symbol(first, Decl(excessPropertyCheckWithUnions.ts, 49, 20)) >third : Symbol(third, Decl(excessPropertyCheckWithUnions.ts, 49, 33)) +// Freshness disappears after spreading a union +declare let t0: { a: any, b: any } | { d: any, e: any } +>t0 : Symbol(t0, Decl(excessPropertyCheckWithUnions.ts, 52, 11)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 52, 17)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 52, 25)) +>d : Symbol(d, Decl(excessPropertyCheckWithUnions.ts, 52, 38)) +>e : Symbol(e, Decl(excessPropertyCheckWithUnions.ts, 52, 46)) + +declare let t1: { a: any, b: any, c: any } | { c: any, d: any, e: any } +>t1 : Symbol(t1, Decl(excessPropertyCheckWithUnions.ts, 53, 11)) +>a : Symbol(a, Decl(excessPropertyCheckWithUnions.ts, 53, 17)) +>b : Symbol(b, Decl(excessPropertyCheckWithUnions.ts, 53, 25)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 53, 33)) +>c : Symbol(c, Decl(excessPropertyCheckWithUnions.ts, 53, 46)) +>d : Symbol(d, Decl(excessPropertyCheckWithUnions.ts, 53, 54)) +>e : Symbol(e, Decl(excessPropertyCheckWithUnions.ts, 53, 62)) + +let t2 = { ...t1 } +>t2 : Symbol(t2, Decl(excessPropertyCheckWithUnions.ts, 54, 3)) +>t1 : Symbol(t1, Decl(excessPropertyCheckWithUnions.ts, 53, 11)) + +t0 = t2 +>t0 : Symbol(t0, Decl(excessPropertyCheckWithUnions.ts, 52, 11)) +>t2 : Symbol(t2, Decl(excessPropertyCheckWithUnions.ts, 54, 3)) + diff --git a/tests/baselines/reference/excessPropertyCheckWithUnions.types b/tests/baselines/reference/excessPropertyCheckWithUnions.types index 1d6bdd32eb269..78f5c025b380d 100644 --- a/tests/baselines/reference/excessPropertyCheckWithUnions.types +++ b/tests/baselines/reference/excessPropertyCheckWithUnions.types @@ -194,3 +194,30 @@ over = { a: 1, b: 1, first: "ok", third: "error" } >third : string >"error" : "error" +// Freshness disappears after spreading a union +declare let t0: { a: any, b: any } | { d: any, e: any } +>t0 : { a: any; b: any; } | { d: any; e: any; } +>a : any +>b : any +>d : any +>e : any + +declare let t1: { a: any, b: any, c: any } | { c: any, d: any, e: any } +>t1 : { a: any; b: any; c: any; } | { c: any; d: any; e: any; } +>a : any +>b : any +>c : any +>c : any +>d : any +>e : any + +let t2 = { ...t1 } +>t2 : { a: any; b: any; c: any; } | { c: any; d: any; e: any; } +>{ ...t1 } : { a: any; b: any; c: any; } | { c: any; d: any; e: any; } +>t1 : { a: any; b: any; c: any; } | { c: any; d: any; e: any; } + +t0 = t2 +>t0 = t2 : { a: any; b: any; c: any; } | { c: any; d: any; e: any; } +>t0 : { a: any; b: any; } | { d: any; e: any; } +>t2 : { a: any; b: any; c: any; } | { c: any; d: any; e: any; } + diff --git a/tests/baselines/reference/spreadInvalidArgumentType.types b/tests/baselines/reference/spreadInvalidArgumentType.types index 244d851589331..ae85189962215 100644 --- a/tests/baselines/reference/spreadInvalidArgumentType.types +++ b/tests/baselines/reference/spreadInvalidArgumentType.types @@ -89,7 +89,7 @@ function f(p1: T, p2: T[]) { >p1 : T var o2 = { ...p2 }; // OK ->o2 : { [n: number]: T; length: number; toString(): string; toLocaleString(): string; push(...items: T[]): number; pop(): T; concat(...items: ReadonlyArray[]): T[]; concat(...items: (T | ReadonlyArray)[]): T[]; join(separator?: string): string; reverse(): T[]; shift(): T; slice(start?: number, end?: number): T[]; sort(compareFn?: (a: T, b: T) => number): T[]; splice(start: number, deleteCount?: number): T[]; splice(start: number, deleteCount: number, ...items: T[]): T[]; unshift(...items: T[]): number; indexOf(searchElement: T, fromIndex?: number): number; lastIndexOf(searchElement: T, fromIndex?: number): number; every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; filter(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]; filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[]; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; } +>o2 : { [x: number]: T; length: number; toString(): string; toLocaleString(): string; push(...items: T[]): number; pop(): T; concat(...items: ReadonlyArray[]): T[]; concat(...items: (T | ReadonlyArray)[]): T[]; join(separator?: string): string; reverse(): T[]; shift(): T; slice(start?: number, end?: number): T[]; sort(compareFn?: (a: T, b: T) => number): T[]; splice(start: number, deleteCount?: number): T[]; splice(start: number, deleteCount: number, ...items: T[]): T[]; unshift(...items: T[]): number; indexOf(searchElement: T, fromIndex?: number): number; lastIndexOf(searchElement: T, fromIndex?: number): number; every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; filter(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]; filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[]; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; } >{ ...p2 } : { [n: number]: T; length: number; toString(): string; toLocaleString(): string; push(...items: T[]): number; pop(): T; concat(...items: ReadonlyArray[]): T[]; concat(...items: (T | ReadonlyArray)[]): T[]; join(separator?: string): string; reverse(): T[]; shift(): T; slice(start?: number, end?: number): T[]; sort(compareFn?: (a: T, b: T) => number): T[]; splice(start: number, deleteCount?: number): T[]; splice(start: number, deleteCount: number, ...items: T[]): T[]; unshift(...items: T[]): number; indexOf(searchElement: T, fromIndex?: number): number; lastIndexOf(searchElement: T, fromIndex?: number): number; every(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; some(callbackfn: (value: T, index: number, array: T[]) => boolean, thisArg?: any): boolean; forEach(callbackfn: (value: T, index: number, array: T[]) => void, thisArg?: any): void; map(callbackfn: (value: T, index: number, array: T[]) => U, thisArg?: any): U[]; filter(callbackfn: (value: T, index: number, array: T[]) => value is S, thisArg?: any): S[]; filter(callbackfn: (value: T, index: number, array: T[]) => any, thisArg?: any): T[]; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduce(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; reduce(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T): T; reduceRight(callbackfn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T, initialValue: T): T; reduceRight(callbackfn: (previousValue: U, currentValue: T, currentIndex: number, array: T[]) => U, initialValue: U): U; } >p2 : T[] diff --git a/tests/cases/compiler/excessPropertyCheckWithUnions.ts b/tests/cases/compiler/excessPropertyCheckWithUnions.ts index 9a7968fe5114e..d5a2327380e76 100644 --- a/tests/cases/compiler/excessPropertyCheckWithUnions.ts +++ b/tests/cases/compiler/excessPropertyCheckWithUnions.ts @@ -1,3 +1,4 @@ +// @strict: true type ADT = { tag: "A", a1: string @@ -48,3 +49,9 @@ let over: Overlapping // these two are not reported because there are two discriminant properties over = { a: 1, b: 1, first: "ok", second: "error" } over = { a: 1, b: 1, first: "ok", third: "error" } + +// Freshness disappears after spreading a union +declare let t0: { a: any, b: any } | { d: any, e: any } +declare let t1: { a: any, b: any, c: any } | { c: any, d: any, e: any } +let t2 = { ...t1 } +t0 = t2