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.
This commit is contained in:
Nathan Shively-Sanders 2017-06-14 10:31:11 -07:00
parent fbe002a595
commit 657c469d4f
4 changed files with 62 additions and 20 deletions

View file

@ -7716,7 +7716,7 @@ namespace ts {
const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations); const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations);
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional); const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
const result = createSymbol(flags, leftProp.name); 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.leftSpread = leftProp;
result.rightSpread = rightProp; result.rightSpread = rightProp;
result.declarations = declarations; result.declarations = declarations;

View file

@ -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; }'. 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. Types of property 'sn' are incompatible.
Type 'number | undefined' is not assignable to type 'string | number'. 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'. 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. Types of property 'title' are incompatible.
Type 'undefined' is not assignable to type 'string'. 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( function f(
definiteBoolean: { sn: boolean }, definiteBoolean: { sn: boolean },
definiteString: { sn: string }, definiteString: { sn: string },
@ -33,17 +29,7 @@ tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS23
undefinedNumber: { sn: number | undefined }) { undefinedNumber: { sn: number | undefined }) {
// optional // optional
let optionalUnionStops: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalNumber }; 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 }; 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 }; let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber };
// undefined // 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: Type '{ title: undefined; yearReleased: number; }' is not assignable to type 'Movie'.
!!! error TS2322: Types of property 'title' are incompatible. !!! error TS2322: Types of property 'title' are incompatible.
!!! error TS2322: Type 'undefined' is not assignable to type 'string'. !!! 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<Fields>, 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'.
}

View file

@ -27,6 +27,21 @@ type Movie = {
const m = { title: "The Matrix", yearReleased: 1999 }; const m = { title: "The Matrix", yearReleased: 1999 };
// should error here because title: undefined is not assignable to string // should error here because title: undefined is not assignable to string
const x: Movie = { ...m, title: undefined }; 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<Fields>, nearlyPartialFields: NearlyPartialFields) {
// ok, undefined is stripped from optional properties when spread
fields = { ...fields, ...partialFields };
// error: not optional, undefined remains
fields = { ...fields, ...nearlyPartialFields };
}
//// [objectSpreadStrictNull.js] //// [objectSpreadStrictNull.js]
@ -52,3 +67,9 @@ function f(definiteBoolean, definiteString, optionalString, optionalNumber, unde
var m = { title: "The Matrix", yearReleased: 1999 }; var m = { title: "The Matrix", yearReleased: 1999 };
// should error here because title: undefined is not assignable to string // should error here because title: undefined is not assignable to string
var x = __assign({}, m, { title: undefined }); 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);
}

View file

@ -28,3 +28,18 @@ type Movie = {
const m = { title: "The Matrix", yearReleased: 1999 }; const m = { title: "The Matrix", yearReleased: 1999 };
// should error here because title: undefined is not assignable to string // should error here because title: undefined is not assignable to string
const x: Movie = { ...m, title: undefined }; 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<Fields>, nearlyPartialFields: NearlyPartialFields) {
// ok, undefined is stripped from optional properties when spread
fields = { ...fields, ...partialFields };
// error: not optional, undefined remains
fields = { ...fields, ...nearlyPartialFields };
}