From 657c469d4ff25e1aa12311d9e2629c4b78067b28 Mon Sep 17 00:00:00 2001 From: Nathan Shively-Sanders Date: Wed, 14 Jun 2017 10:31:11 -0700 Subject: [PATCH] Remove undefined from optional spread properties Fixes #16509 by making the change from #15938 less strict. This is technically a hole, but it's not as big a hole as before #15938. --- src/compiler/checker.ts | 2 +- .../objectSpreadStrictNull.errors.txt | 44 +++++++++++-------- .../reference/objectSpreadStrictNull.js | 21 +++++++++ .../types/spread/objectSpreadStrictNull.ts | 15 +++++++ 4 files changed, 62 insertions(+), 20 deletions(-) diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index c931fddb83..4687856c60 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -7716,7 +7716,7 @@ namespace ts { const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations); const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional); const result = createSymbol(flags, leftProp.name); - result.type = getUnionType([getTypeOfSymbol(leftProp), rightType]); + result.type = getUnionType([getTypeOfSymbol(leftProp), getTypeWithFacts(rightType, TypeFacts.NEUndefined)]); result.leftSpread = leftProp; result.rightSpread = rightProp; result.declarations = declarations; diff --git a/tests/baselines/reference/objectSpreadStrictNull.errors.txt b/tests/baselines/reference/objectSpreadStrictNull.errors.txt index f3c5cc7de2..791b5419eb 100644 --- a/tests/baselines/reference/objectSpreadStrictNull.errors.txt +++ b/tests/baselines/reference/objectSpreadStrictNull.errors.txt @@ -1,11 +1,3 @@ -tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(9,9): error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'. - Types of property 'sn' are incompatible. - Type 'string | number | undefined' is not assignable to type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. -tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(10,9): error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'. - Types of property 'sn' are incompatible. - Type 'string | number | undefined' is not assignable to type 'string | number'. - Type 'undefined' is not assignable to type 'string | number'. tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(14,9): error TS2322: Type '{ sn: number | undefined; }' is not assignable to type '{ sn: string | number; }'. Types of property 'sn' are incompatible. Type 'number | undefined' is not assignable to type 'string | number'. @@ -21,9 +13,13 @@ tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(18,9): error TS23 tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS2322: Type '{ title: undefined; yearReleased: number; }' is not assignable to type 'Movie'. Types of property 'title' are incompatible. Type 'undefined' is not assignable to type 'string'. +tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(42,5): error TS2322: Type '{ foo: number | undefined; bar: string | undefined; }' is not assignable to type 'Fields'. + Types of property 'foo' are incompatible. + Type 'number | undefined' is not assignable to type 'number'. + Type 'undefined' is not assignable to type 'number'. -==== tests/cases/conformance/types/spread/objectSpreadStrictNull.ts (6 errors) ==== +==== tests/cases/conformance/types/spread/objectSpreadStrictNull.ts (5 errors) ==== function f( definiteBoolean: { sn: boolean }, definiteString: { sn: string }, @@ -33,17 +29,7 @@ tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS23 undefinedNumber: { sn: number | undefined }) { // optional let optionalUnionStops: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalNumber }; - ~~~~~~~~~~~~~~~~~~ -!!! error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'. -!!! error TS2322: Types of property 'sn' are incompatible. -!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'string | number'. -!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'. let optionalUnionDuplicates: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber }; - ~~~~~~~~~~~~~~~~~~~~~~~ -!!! error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'. -!!! error TS2322: Types of property 'sn' are incompatible. -!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'string | number'. -!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'. let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber }; // undefined @@ -81,4 +67,24 @@ tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS23 !!! error TS2322: Type '{ title: undefined; yearReleased: number; }' is not assignable to type 'Movie'. !!! error TS2322: Types of property 'title' are incompatible. !!! error TS2322: Type 'undefined' is not assignable to type 'string'. + + interface Fields { + foo: number; + bar: string; + } + interface NearlyPartialFields { + foo: number | undefined; + bar: string | undefined; + } + function g(fields: Fields, partialFields: Partial, nearlyPartialFields: NearlyPartialFields) { + // ok, undefined is stripped from optional properties when spread + fields = { ...fields, ...partialFields }; + // error: not optional, undefined remains + fields = { ...fields, ...nearlyPartialFields }; + ~~~~~~ +!!! error TS2322: Type '{ foo: number | undefined; bar: string | undefined; }' is not assignable to type 'Fields'. +!!! error TS2322: Types of property 'foo' are incompatible. +!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'. +!!! error TS2322: Type 'undefined' is not assignable to type 'number'. + } \ No newline at end of file diff --git a/tests/baselines/reference/objectSpreadStrictNull.js b/tests/baselines/reference/objectSpreadStrictNull.js index a21b5e2467..521d83f37a 100644 --- a/tests/baselines/reference/objectSpreadStrictNull.js +++ b/tests/baselines/reference/objectSpreadStrictNull.js @@ -27,6 +27,21 @@ type Movie = { const m = { title: "The Matrix", yearReleased: 1999 }; // should error here because title: undefined is not assignable to string const x: Movie = { ...m, title: undefined }; + +interface Fields { + foo: number; + bar: string; +} +interface NearlyPartialFields { + foo: number | undefined; + bar: string | undefined; +} +function g(fields: Fields, partialFields: Partial, nearlyPartialFields: NearlyPartialFields) { + // ok, undefined is stripped from optional properties when spread + fields = { ...fields, ...partialFields }; + // error: not optional, undefined remains + fields = { ...fields, ...nearlyPartialFields }; +} //// [objectSpreadStrictNull.js] @@ -52,3 +67,9 @@ function f(definiteBoolean, definiteString, optionalString, optionalNumber, unde var m = { title: "The Matrix", yearReleased: 1999 }; // should error here because title: undefined is not assignable to string var x = __assign({}, m, { title: undefined }); +function g(fields, partialFields, nearlyPartialFields) { + // ok, undefined is stripped from optional properties when spread + fields = __assign({}, fields, partialFields); + // error: not optional, undefined remains + fields = __assign({}, fields, nearlyPartialFields); +} diff --git a/tests/cases/conformance/types/spread/objectSpreadStrictNull.ts b/tests/cases/conformance/types/spread/objectSpreadStrictNull.ts index 979a6feed0..9ccc7ea65b 100644 --- a/tests/cases/conformance/types/spread/objectSpreadStrictNull.ts +++ b/tests/cases/conformance/types/spread/objectSpreadStrictNull.ts @@ -28,3 +28,18 @@ type Movie = { const m = { title: "The Matrix", yearReleased: 1999 }; // should error here because title: undefined is not assignable to string const x: Movie = { ...m, title: undefined }; + +interface Fields { + foo: number; + bar: string; +} +interface NearlyPartialFields { + foo: number | undefined; + bar: string | undefined; +} +function g(fields: Fields, partialFields: Partial, nearlyPartialFields: NearlyPartialFields) { + // ok, undefined is stripped from optional properties when spread + fields = { ...fields, ...partialFields }; + // error: not optional, undefined remains + fields = { ...fields, ...nearlyPartialFields }; +}