diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 148345c8ef..bd48701de4 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -9806,7 +9806,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, typeFlags: TypeFlags, objectFlags: ObjectFlags): Type { + function getSpreadType(left: Type, right: Type, symbol: Symbol | undefined, typeFlags: TypeFlags, objectFlags: ObjectFlags, isUnion?: boolean): Type { if (left.flags & TypeFlags.Any || right.flags & TypeFlags.Any) { return anyType; } @@ -9820,10 +9820,10 @@ namespace ts { return left; } if (left.flags & TypeFlags.Union) { - return mapType(left, t => getSpreadType(t, right, symbol, typeFlags, objectFlags)); + return mapType(left, t => getSpreadType(t, right, symbol, typeFlags, objectFlags, isUnion)); } if (right.flags & TypeFlags.Union) { - return mapType(right, t => getSpreadType(left, t, symbol, typeFlags, objectFlags)); + return mapType(right, t => getSpreadType(left, t, symbol, typeFlags, objectFlags, true)); } if (right.flags & (TypeFlags.BooleanLike | TypeFlags.NumberLike | TypeFlags.StringLike | TypeFlags.EnumLike | TypeFlags.NonPrimitive | TypeFlags.Index)) { return left; @@ -9870,7 +9870,12 @@ namespace ts { result.nameType = leftProp.nameType; members.set(leftProp.escapedName, result); } - else if (symbol && shouldCheckAsExcessProperty(leftProp, symbol) && !shouldCheckAsExcessProperty(rightProp, symbol)) { + else if (strictNullChecks && + !isUnion && + symbol && + shouldCheckAsExcessProperty(leftProp, symbol) && + !shouldCheckAsExcessProperty(rightProp, symbol) && + !(getFalsyFlags(rightType) & TypeFlags.Nullable)) { error(leftProp.valueDeclaration, Diagnostics._0_is_overwritten_by_a_later_spread, unescapeLeadingUnderscores(leftProp.escapedName)); } } diff --git a/tests/baselines/reference/checkJsxChildrenProperty13.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty13.errors.txt deleted file mode 100644 index d375a6e0c0..0000000000 --- a/tests/baselines/reference/checkJsxChildrenProperty13.errors.txt +++ /dev/null @@ -1,33 +0,0 @@ -tests/cases/conformance/jsx/file.tsx(12,46): error TS2735: 'children' is overwritten by a later spread. - - -==== tests/cases/conformance/jsx/file.tsx (1 errors) ==== - import React = require('react'); - - interface ButtonProp { - a: number, - b: string, - children: Button; - } - - class Button extends React.Component { - render() { - // Error children are specified twice - return ( - ~~~~~~~~~~~~~ -!!! error TS2735: 'children' is overwritten by a later spread. -
Hello World
-
); - } - } - - interface InnerButtonProp { - a: number - } - - class InnerButton extends React.Component { - render() { - return (); - } - } - \ No newline at end of file diff --git a/tests/baselines/reference/checkJsxChildrenProperty2.errors.txt b/tests/baselines/reference/checkJsxChildrenProperty2.errors.txt index 38f6fae244..0282c566e4 100644 --- a/tests/baselines/reference/checkJsxChildrenProperty2.errors.txt +++ b/tests/baselines/reference/checkJsxChildrenProperty2.errors.txt @@ -1,6 +1,5 @@ tests/cases/conformance/jsx/file.tsx(14,10): error TS2322: Type '{ a: number; b: string; }' is not assignable to type 'Prop'. Property 'children' is missing in type '{ a: number; b: string; }'. -tests/cases/conformance/jsx/file.tsx(17,25): error TS2735: 'children' is overwritten by a later spread. tests/cases/conformance/jsx/file.tsx(31,6): error TS2322: Type '{ children: (Element | ((name: string) => Element))[]; a: number; b: string; }' is not assignable to type 'Prop'. Types of property 'children' are incompatible. Type '(Element | ((name: string) => Element))[]' is not assignable to type 'string | Element'. @@ -23,7 +22,7 @@ tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Elem Property 'type' is missing in type 'Element[]'. -==== tests/cases/conformance/jsx/file.tsx (6 errors) ==== +==== tests/cases/conformance/jsx/file.tsx (5 errors) ==== import React = require('react'); interface Prop { @@ -44,8 +43,6 @@ tests/cases/conformance/jsx/file.tsx(49,6): error TS2322: Type '{ children: Elem let k0 = - ~~~~~~~~~~~~~~~~~ -!!! error TS2735: 'children' is overwritten by a later spread. hi hi hi! ; diff --git a/tests/baselines/reference/objectSpreadNegative.errors.txt b/tests/baselines/reference/objectSpreadNegative.errors.txt index 57de2e1797..0d6a58fe76 100644 --- a/tests/baselines/reference/objectSpreadNegative.errors.txt +++ b/tests/baselines/reference/objectSpreadNegative.errors.txt @@ -5,15 +5,8 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(23,1): error TS2322 Property 'b' is missing in type '{ s: string; }'. tests/cases/conformance/types/spread/objectSpreadNegative.ts(25,1): error TS2322: Type '{ b: boolean; }' is not assignable to type '{ s: string; b: boolean; }'. Property 's' is missing in type '{ b: boolean; }'. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(27,20): error TS2735: 'b' is overwritten by a later spread. tests/cases/conformance/types/spread/objectSpreadNegative.ts(27,36): error TS2300: Duplicate identifier 'b'. tests/cases/conformance/types/spread/objectSpreadNegative.ts(27,53): error TS2300: Duplicate identifier 'b'. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(31,7): error TS2735: 'b' is overwritten by a later spread. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(36,7): error TS2735: 'b' is overwritten by a later spread. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(38,14): error TS2735: 'b' is overwritten by a later spread. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(40,53): error TS2735: 'd' is overwritten by a later spread. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(42,7): error TS2735: 'a' is overwritten by a later spread. -tests/cases/conformance/types/spread/objectSpreadNegative.ts(44,37): error TS2735: 'b' is overwritten by a later spread. tests/cases/conformance/types/spread/objectSpreadNegative.ts(47,19): error TS2698: Spread types may only be created from object types. tests/cases/conformance/types/spread/objectSpreadNegative.ts(48,19): error TS2698: Spread types may only be created from object types. tests/cases/conformance/types/spread/objectSpreadNegative.ts(49,20): error TS2698: Spread types may only be created from object types. @@ -27,7 +20,7 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(77,14): error TS269 tests/cases/conformance/types/spread/objectSpreadNegative.ts(80,14): error TS2698: Spread types may only be created from object types. -==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (24 errors) ==== +==== tests/cases/conformance/types/spread/objectSpreadNegative.ts (17 errors) ==== let o = { a: 1, b: 'no' } /// private propagates @@ -66,8 +59,6 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(80,14): error TS269 !!! error TS2322: Property 's' is missing in type '{ b: boolean; }'. let duplicated = { b: 'bad', ...o, b: 'bad', ...o2, b: 'bad' } - ~~~~~~~~ -!!! error TS2735: 'b' is overwritten by a later spread. ~ !!! error TS2300: Duplicate identifier 'b'. ~ @@ -76,31 +67,19 @@ tests/cases/conformance/types/spread/objectSpreadNegative.ts(80,14): error TS269 // Note: ignore changes the order that properties are printed let ignore: { a: number, b: string } = { b: 'ignored', ...o } - ~~~~~~~~~~~~ -!!! error TS2735: 'b' is overwritten by a later spread. let o3 = { a: 1, b: 'no' } let o4 = { b: 'yes', c: true } let combinedBefore: { a: number, b: string, c: boolean } = { b: 'ok', ...o3, ...o4 } - ~~~~~~~ -!!! error TS2735: 'b' is overwritten by a later spread. let combinedMid: { a: number, b: string, c: boolean } = { ...o3, b: 'ok', ...o4 } - ~~~~~~~ -!!! error TS2735: 'b' is overwritten by a later spread. let combinedNested: { a: number, b: boolean, c: string, d: string } = { ...{ a: 4, ...{ b: false, c: 'overriden' } }, d: 'actually new', ...{ a: 5, d: 'maybe new' } } - ~~~~~~~~~~~~~~~~~ -!!! error TS2735: 'd' is overwritten by a later spread. let changeTypeBefore: { a: number, b: string } = { a: 'wrong type?', ...o3 }; - ~~~~~~~~~~~~~~~~ -!!! error TS2735: 'a' is overwritten by a later spread. let computedMiddle: { a: number, b: string, c: boolean, "in the middle": number } = { ...o3, ['in the middle']: 13, b: 'maybe?', ...o4 } - ~~~~~~~~~~~ -!!! error TS2735: 'b' is overwritten by a later spread. // primitives are not allowed, except for falsy ones let spreadNum = { ...12 }; diff --git a/tests/baselines/reference/spreadOverwritesProperty.errors.txt b/tests/baselines/reference/spreadOverwritesProperty.errors.txt deleted file mode 100644 index a6b78900b4..0000000000 --- a/tests/baselines/reference/spreadOverwritesProperty.errors.txt +++ /dev/null @@ -1,12 +0,0 @@ -tests/cases/conformance/types/spread/spreadOverwritesProperty.ts(3,17): error TS2735: 'b' is overwritten by a later spread. - - -==== tests/cases/conformance/types/spread/spreadOverwritesProperty.ts (1 errors) ==== - declare var ab: { a: number, b: number }; - declare var abq: { a: number, b?: number }; - var unused1 = { b: 1, ...ab } - ~~~~ -!!! error TS2735: 'b' is overwritten by a later spread. - var unused2 = { ...ab, ...ab } - var unused3 = { b: 1, ...abq } - \ No newline at end of file diff --git a/tests/baselines/reference/spreadOverwritesProperty.js b/tests/baselines/reference/spreadOverwritesProperty.js index ac28594c4b..653bc11367 100644 --- a/tests/baselines/reference/spreadOverwritesProperty.js +++ b/tests/baselines/reference/spreadOverwritesProperty.js @@ -4,6 +4,13 @@ declare var abq: { a: number, b?: number }; var unused1 = { b: 1, ...ab } var unused2 = { ...ab, ...ab } var unused3 = { b: 1, ...abq } + +function g(obj: { x: number | undefined }) { + return { x: 1, ...obj }; // should be allowed because of undefined +} +function h(obj: { x: number }) { + return { x: 1, ...obj }; // should be allowed because we don't know about undefined +} //// [spreadOverwritesProperty.js] @@ -21,3 +28,9 @@ var __assign = (this && this.__assign) || function () { var unused1 = __assign({ b: 1 }, ab); var unused2 = __assign({}, ab, ab); var unused3 = __assign({ b: 1 }, abq); +function g(obj) { + return __assign({ x: 1 }, obj); // should be allowed because of undefined +} +function h(obj) { + return __assign({ x: 1 }, obj); // should be allowed because we don't know about undefined +} diff --git a/tests/baselines/reference/spreadOverwritesProperty.symbols b/tests/baselines/reference/spreadOverwritesProperty.symbols index 599a40c855..7af75e14d3 100644 --- a/tests/baselines/reference/spreadOverwritesProperty.symbols +++ b/tests/baselines/reference/spreadOverwritesProperty.symbols @@ -24,3 +24,22 @@ var unused3 = { b: 1, ...abq } >b : Symbol(b, Decl(spreadOverwritesProperty.ts, 4, 15)) >abq : Symbol(abq, Decl(spreadOverwritesProperty.ts, 1, 11)) +function g(obj: { x: number | undefined }) { +>g : Symbol(g, Decl(spreadOverwritesProperty.ts, 4, 30)) +>obj : Symbol(obj, Decl(spreadOverwritesProperty.ts, 6, 11)) +>x : Symbol(x, Decl(spreadOverwritesProperty.ts, 6, 17)) + + return { x: 1, ...obj }; // should be allowed because of undefined +>x : Symbol(x, Decl(spreadOverwritesProperty.ts, 7, 12)) +>obj : Symbol(obj, Decl(spreadOverwritesProperty.ts, 6, 11)) +} +function h(obj: { x: number }) { +>h : Symbol(h, Decl(spreadOverwritesProperty.ts, 8, 1)) +>obj : Symbol(obj, Decl(spreadOverwritesProperty.ts, 9, 11)) +>x : Symbol(x, Decl(spreadOverwritesProperty.ts, 9, 17)) + + return { x: 1, ...obj }; // should be allowed because we don't know about undefined +>x : Symbol(x, Decl(spreadOverwritesProperty.ts, 10, 12)) +>obj : Symbol(obj, Decl(spreadOverwritesProperty.ts, 9, 11)) +} + diff --git a/tests/baselines/reference/spreadOverwritesProperty.types b/tests/baselines/reference/spreadOverwritesProperty.types index 9d6630a406..0dad333c3a 100644 --- a/tests/baselines/reference/spreadOverwritesProperty.types +++ b/tests/baselines/reference/spreadOverwritesProperty.types @@ -29,3 +29,26 @@ var unused3 = { b: 1, ...abq } >1 : 1 >abq : { a: number; b?: number; } +function g(obj: { x: number | undefined }) { +>g : (obj: { x: number; }) => { x: number; } +>obj : { x: number; } +>x : number + + return { x: 1, ...obj }; // should be allowed because of undefined +>{ x: 1, ...obj } : { x: number; } +>x : number +>1 : 1 +>obj : { x: number; } +} +function h(obj: { x: number }) { +>h : (obj: { x: number; }) => { x: number; } +>obj : { x: number; } +>x : number + + return { x: 1, ...obj }; // should be allowed because we don't know about undefined +>{ x: 1, ...obj } : { x: number; } +>x : number +>1 : 1 +>obj : { x: number; } +} + diff --git a/tests/baselines/reference/spreadOverwritesPropertyStrict.errors.txt b/tests/baselines/reference/spreadOverwritesPropertyStrict.errors.txt index 5a3706479a..70febf0086 100644 --- a/tests/baselines/reference/spreadOverwritesPropertyStrict.errors.txt +++ b/tests/baselines/reference/spreadOverwritesPropertyStrict.errors.txt @@ -9,4 +9,10 @@ tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts(3,17): er !!! error TS2735: 'b' is overwritten by a later spread. var unused2 = { ...ab, ...ab } var unused3 = { b: 1, ...abq } + function g(obj: { x: number | undefined }) { + return { x: 1, ...obj }; // should be allowed because of undefined + } + function f(obj: { x: number } | undefined) { + return { x: 1, ...obj }; + } \ No newline at end of file diff --git a/tests/baselines/reference/spreadOverwritesPropertyStrict.js b/tests/baselines/reference/spreadOverwritesPropertyStrict.js index 0767afcb58..457bc5ed7e 100644 --- a/tests/baselines/reference/spreadOverwritesPropertyStrict.js +++ b/tests/baselines/reference/spreadOverwritesPropertyStrict.js @@ -4,6 +4,12 @@ declare var abq: { a: number, b?: number }; var unused1 = { b: 1, ...ab } var unused2 = { ...ab, ...ab } var unused3 = { b: 1, ...abq } +function g(obj: { x: number | undefined }) { + return { x: 1, ...obj }; // should be allowed because of undefined +} +function f(obj: { x: number } | undefined) { + return { x: 1, ...obj }; +} //// [spreadOverwritesPropertyStrict.js] @@ -22,3 +28,9 @@ var __assign = (this && this.__assign) || function () { var unused1 = __assign({ b: 1 }, ab); var unused2 = __assign({}, ab, ab); var unused3 = __assign({ b: 1 }, abq); +function g(obj) { + return __assign({ x: 1 }, obj); // should be allowed because of undefined +} +function f(obj) { + return __assign({ x: 1 }, obj); +} diff --git a/tests/baselines/reference/spreadOverwritesPropertyStrict.symbols b/tests/baselines/reference/spreadOverwritesPropertyStrict.symbols index 0f03d28fdc..0168d14296 100644 --- a/tests/baselines/reference/spreadOverwritesPropertyStrict.symbols +++ b/tests/baselines/reference/spreadOverwritesPropertyStrict.symbols @@ -24,3 +24,22 @@ var unused3 = { b: 1, ...abq } >b : Symbol(b, Decl(spreadOverwritesPropertyStrict.ts, 4, 15)) >abq : Symbol(abq, Decl(spreadOverwritesPropertyStrict.ts, 1, 11)) +function g(obj: { x: number | undefined }) { +>g : Symbol(g, Decl(spreadOverwritesPropertyStrict.ts, 4, 30)) +>obj : Symbol(obj, Decl(spreadOverwritesPropertyStrict.ts, 5, 11)) +>x : Symbol(x, Decl(spreadOverwritesPropertyStrict.ts, 5, 17)) + + return { x: 1, ...obj }; // should be allowed because of undefined +>x : Symbol(x, Decl(spreadOverwritesPropertyStrict.ts, 6, 12)) +>obj : Symbol(obj, Decl(spreadOverwritesPropertyStrict.ts, 5, 11)) +} +function f(obj: { x: number } | undefined) { +>f : Symbol(f, Decl(spreadOverwritesPropertyStrict.ts, 7, 1)) +>obj : Symbol(obj, Decl(spreadOverwritesPropertyStrict.ts, 8, 11)) +>x : Symbol(x, Decl(spreadOverwritesPropertyStrict.ts, 8, 17)) + + return { x: 1, ...obj }; +>x : Symbol(x, Decl(spreadOverwritesPropertyStrict.ts, 9, 12)) +>obj : Symbol(obj, Decl(spreadOverwritesPropertyStrict.ts, 8, 11)) +} + diff --git a/tests/baselines/reference/spreadOverwritesPropertyStrict.types b/tests/baselines/reference/spreadOverwritesPropertyStrict.types index f5a5492727..64350c3985 100644 --- a/tests/baselines/reference/spreadOverwritesPropertyStrict.types +++ b/tests/baselines/reference/spreadOverwritesPropertyStrict.types @@ -29,3 +29,26 @@ var unused3 = { b: 1, ...abq } >1 : 1 >abq : { a: number; b?: number | undefined; } +function g(obj: { x: number | undefined }) { +>g : (obj: { x: number | undefined; }) => { x: number | undefined; } +>obj : { x: number | undefined; } +>x : number | undefined + + return { x: 1, ...obj }; // should be allowed because of undefined +>{ x: 1, ...obj } : { x: number | undefined; } +>x : number +>1 : 1 +>obj : { x: number | undefined; } +} +function f(obj: { x: number } | undefined) { +>f : (obj: { x: number; } | undefined) => { x: number; } | { x: number; } +>obj : { x: number; } | undefined +>x : number + + return { x: 1, ...obj }; +>{ x: 1, ...obj } : { x: number; } | { x: number; } +>x : number +>1 : 1 +>obj : { x: number; } | undefined +} + diff --git a/tests/baselines/reference/spreadUnion3.errors.txt b/tests/baselines/reference/spreadUnion3.errors.txt index b9c7c186fe..88aa12c708 100644 --- a/tests/baselines/reference/spreadUnion3.errors.txt +++ b/tests/baselines/reference/spreadUnion3.errors.txt @@ -1,18 +1,15 @@ tests/cases/conformance/types/spread/spreadUnion3.ts(2,14): error TS2322: Type 'number' is not assignable to type 'string'. -tests/cases/conformance/types/spread/spreadUnion3.ts(2,14): error TS2735: 'y' is overwritten by a later spread. tests/cases/conformance/types/spread/spreadUnion3.ts(9,23): error TS2339: Property 'a' does not exist on type '{}'. tests/cases/conformance/types/spread/spreadUnion3.ts(17,11): error TS2698: Spread types may only be created from object types. tests/cases/conformance/types/spread/spreadUnion3.ts(18,11): error TS2698: Spread types may only be created from object types. -==== tests/cases/conformance/types/spread/spreadUnion3.ts (5 errors) ==== +==== tests/cases/conformance/types/spread/spreadUnion3.ts (4 errors) ==== function f(x: { y: string } | undefined): { y: string } { return { y: 123, ...x } // y: string | number ~ !!! error TS2322: Type 'number' is not assignable to type 'string'. !!! related TS6500 tests/cases/conformance/types/spread/spreadUnion3.ts:1:45: The expected type comes from property 'y' which is declared here on type '{ y: string; }' - ~~~~~~ -!!! error TS2735: 'y' is overwritten by a later spread. } f(undefined) diff --git a/tests/cases/conformance/types/spread/spreadOverwritesProperty.ts b/tests/cases/conformance/types/spread/spreadOverwritesProperty.ts index e4407fd87e..4ebc7f9fa0 100644 --- a/tests/cases/conformance/types/spread/spreadOverwritesProperty.ts +++ b/tests/cases/conformance/types/spread/spreadOverwritesProperty.ts @@ -3,3 +3,10 @@ declare var abq: { a: number, b?: number }; var unused1 = { b: 1, ...ab } var unused2 = { ...ab, ...ab } var unused3 = { b: 1, ...abq } + +function g(obj: { x: number | undefined }) { + return { x: 1, ...obj }; // should be allowed because of undefined +} +function h(obj: { x: number }) { + return { x: 1, ...obj }; // should be allowed because we don't know about undefined +} diff --git a/tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts b/tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts index d1df11e49b..e2bf6da240 100644 --- a/tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts +++ b/tests/cases/conformance/types/spread/spreadOverwritesPropertyStrict.ts @@ -4,3 +4,9 @@ declare var abq: { a: number, b?: number }; var unused1 = { b: 1, ...ab } var unused2 = { ...ab, ...ab } var unused3 = { b: 1, ...abq } +function g(obj: { x: number | undefined }) { + return { x: 1, ...obj }; // should be allowed because of undefined +} +function f(obj: { x: number } | undefined) { + return { x: 1, ...obj }; +}