Skip to content

Commit 657c469

Browse files
committed
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.
1 parent fbe002a commit 657c469

File tree

4 files changed

+62
-20
lines changed

4 files changed

+62
-20
lines changed

src/compiler/checker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -7716,7 +7716,7 @@ namespace ts {
77167716
const declarations: Declaration[] = concatenate(leftProp.declarations, rightProp.declarations);
77177717
const flags = SymbolFlags.Property | (leftProp.flags & SymbolFlags.Optional);
77187718
const result = createSymbol(flags, leftProp.name);
7719-
result.type = getUnionType([getTypeOfSymbol(leftProp), rightType]);
7719+
result.type = getUnionType([getTypeOfSymbol(leftProp), getTypeWithFacts(rightType, TypeFacts.NEUndefined)]);
77207720
result.leftSpread = leftProp;
77217721
result.rightSpread = rightProp;
77227722
result.declarations = declarations;

tests/baselines/reference/objectSpreadStrictNull.errors.txt

+25-19
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,3 @@
1-
tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(9,9): error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'.
2-
Types of property 'sn' are incompatible.
3-
Type 'string | number | undefined' is not assignable to type 'string | number'.
4-
Type 'undefined' is not assignable to type 'string | number'.
5-
tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(10,9): error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'.
6-
Types of property 'sn' are incompatible.
7-
Type 'string | number | undefined' is not assignable to type 'string | number'.
8-
Type 'undefined' is not assignable to type 'string | number'.
91
tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(14,9): error TS2322: Type '{ sn: number | undefined; }' is not assignable to type '{ sn: string | number; }'.
102
Types of property 'sn' are incompatible.
113
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
2113
tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS2322: Type '{ title: undefined; yearReleased: number; }' is not assignable to type 'Movie'.
2214
Types of property 'title' are incompatible.
2315
Type 'undefined' is not assignable to type 'string'.
16+
tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(42,5): error TS2322: Type '{ foo: number | undefined; bar: string | undefined; }' is not assignable to type 'Fields'.
17+
Types of property 'foo' are incompatible.
18+
Type 'number | undefined' is not assignable to type 'number'.
19+
Type 'undefined' is not assignable to type 'number'.
2420

2521

26-
==== tests/cases/conformance/types/spread/objectSpreadStrictNull.ts (6 errors) ====
22+
==== tests/cases/conformance/types/spread/objectSpreadStrictNull.ts (5 errors) ====
2723
function f(
2824
definiteBoolean: { sn: boolean },
2925
definiteString: { sn: string },
@@ -33,17 +29,7 @@ tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS23
3329
undefinedNumber: { sn: number | undefined }) {
3430
// optional
3531
let optionalUnionStops: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalNumber };
36-
~~~~~~~~~~~~~~~~~~
37-
!!! error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'.
38-
!!! error TS2322: Types of property 'sn' are incompatible.
39-
!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'string | number'.
40-
!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'.
4132
let optionalUnionDuplicates: { sn: string | number } = { ...definiteBoolean, ...definiteString, ...optionalString, ...optionalNumber };
42-
~~~~~~~~~~~~~~~~~~~~~~~
43-
!!! error TS2322: Type '{ sn: string | number | undefined; }' is not assignable to type '{ sn: string | number; }'.
44-
!!! error TS2322: Types of property 'sn' are incompatible.
45-
!!! error TS2322: Type 'string | number | undefined' is not assignable to type 'string | number'.
46-
!!! error TS2322: Type 'undefined' is not assignable to type 'string | number'.
4733
let allOptional: { sn?: string | number } = { ...optionalString, ...optionalNumber };
4834

4935
// undefined
@@ -81,4 +67,24 @@ tests/cases/conformance/types/spread/objectSpreadStrictNull.ts(28,7): error TS23
8167
!!! error TS2322: Type '{ title: undefined; yearReleased: number; }' is not assignable to type 'Movie'.
8268
!!! error TS2322: Types of property 'title' are incompatible.
8369
!!! error TS2322: Type 'undefined' is not assignable to type 'string'.
70+
71+
interface Fields {
72+
foo: number;
73+
bar: string;
74+
}
75+
interface NearlyPartialFields {
76+
foo: number | undefined;
77+
bar: string | undefined;
78+
}
79+
function g(fields: Fields, partialFields: Partial<Fields>, nearlyPartialFields: NearlyPartialFields) {
80+
// ok, undefined is stripped from optional properties when spread
81+
fields = { ...fields, ...partialFields };
82+
// error: not optional, undefined remains
83+
fields = { ...fields, ...nearlyPartialFields };
84+
~~~~~~
85+
!!! error TS2322: Type '{ foo: number | undefined; bar: string | undefined; }' is not assignable to type 'Fields'.
86+
!!! error TS2322: Types of property 'foo' are incompatible.
87+
!!! error TS2322: Type 'number | undefined' is not assignable to type 'number'.
88+
!!! error TS2322: Type 'undefined' is not assignable to type 'number'.
89+
}
8490

tests/baselines/reference/objectSpreadStrictNull.js

+21
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,21 @@ type Movie = {
2727
const m = { title: "The Matrix", yearReleased: 1999 };
2828
// should error here because title: undefined is not assignable to string
2929
const x: Movie = { ...m, title: undefined };
30+
31+
interface Fields {
32+
foo: number;
33+
bar: string;
34+
}
35+
interface NearlyPartialFields {
36+
foo: number | undefined;
37+
bar: string | undefined;
38+
}
39+
function g(fields: Fields, partialFields: Partial<Fields>, nearlyPartialFields: NearlyPartialFields) {
40+
// ok, undefined is stripped from optional properties when spread
41+
fields = { ...fields, ...partialFields };
42+
// error: not optional, undefined remains
43+
fields = { ...fields, ...nearlyPartialFields };
44+
}
3045

3146

3247
//// [objectSpreadStrictNull.js]
@@ -52,3 +67,9 @@ function f(definiteBoolean, definiteString, optionalString, optionalNumber, unde
5267
var m = { title: "The Matrix", yearReleased: 1999 };
5368
// should error here because title: undefined is not assignable to string
5469
var x = __assign({}, m, { title: undefined });
70+
function g(fields, partialFields, nearlyPartialFields) {
71+
// ok, undefined is stripped from optional properties when spread
72+
fields = __assign({}, fields, partialFields);
73+
// error: not optional, undefined remains
74+
fields = __assign({}, fields, nearlyPartialFields);
75+
}

tests/cases/conformance/types/spread/objectSpreadStrictNull.ts

+15
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,18 @@ type Movie = {
2828
const m = { title: "The Matrix", yearReleased: 1999 };
2929
// should error here because title: undefined is not assignable to string
3030
const x: Movie = { ...m, title: undefined };
31+
32+
interface Fields {
33+
foo: number;
34+
bar: string;
35+
}
36+
interface NearlyPartialFields {
37+
foo: number | undefined;
38+
bar: string | undefined;
39+
}
40+
function g(fields: Fields, partialFields: Partial<Fields>, nearlyPartialFields: NearlyPartialFields) {
41+
// ok, undefined is stripped from optional properties when spread
42+
fields = { ...fields, ...partialFields };
43+
// error: not optional, undefined remains
44+
fields = { ...fields, ...nearlyPartialFields };
45+
}

0 commit comments

Comments
 (0)