You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
constx={a: 1}consty: {a?: number}={}constz: {a?: number}={a: undefined}constxy={...x, ...y}constxya=xy.a// numberconsole.log(xya)// 1 - all goodconstxz={...x, ...z}constxza=xz.a// numberconsole.log(xza)// undefined - bad
🙁 Actual behavior
When you spread two objects together that have the same property set, the property gets the last value assigned. This holds true even when the property is assigned undefined. However, TypeScript doesn't differentiate between a property that is not set and a property that is set to undefined. This causes problems when you spread an object with a property set to undefined—TypeScript assumes the property is not set at all, so it skips it and infers from whatever other (earlier in the spread) values the property could take.
In the example above, y and z are indifferentiable to TypeScript, but when you spread them the resulting objects are different. xy.a is in fact a number, as TypeScript thinks it is, because {...x, ...y} uses the value of a from x. xz.a, on the other hand, is undefined, but TypeScript still assumes it's a number because it thinks {...x, ...z} used the value of a from x.
🙂 Expected behavior
I'm really not sure how TypeScript could deal with this case best. It could give a type number | undefined in both scenarios above, but then that's annoying when a really is not set on the object (y in the example).
Ideally I think there would be a difference in types between y and z. TypeScript already does this if the type is inferred—if you remove : { a?: number} from the third line above, x gets type { a: undefined } and xza gets type undefined, which is what it should be. The issue seems to come from the property being explicitly optional but set to undefined.
Enabling exactOptionalPropertyTypes correctly fixes this issue:
index.ts:2:7 - error TS2375: Type '{ val: undefined; }' is not assignable to type '{ val?: string; }' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
Types of property 'val' are incompatible.
Type 'undefined' is not assignable to type 'string'.
2 const a: { val?: string } = {
~
🔎 Search Terms
javascript typescript spread spreading undefined optional property object type
🕗 Version & Regression Information
⏯ Playground Link
https://www.typescriptlang.org/play?ts=5.3.3&ssl=2&ssc=24&pln=2&pc=8#code/MYewdgzgLgBAHjAvDA3gQwFwwIwF8BQoksAnlijGgPxZgCuAtgEYCmATjLkqgUdDAC9ylGjHrN2XZBUww6YACYsAZgEswLBZ3yFw-OCW4oAdKbgAaGKeMlee2AbTcDxpwHo3Yxqza7IIABsWYwCQAHMACkcAShgPHBgAWkoAgJgwkBAFHT4HASNrCytTATtieAEnZDgBVzjPcR8-CEDg0MiatFj4+SU1DS1kpjQFIA
💻 Code
🙁 Actual behavior
When you spread two objects together that have the same property set, the property gets the last value assigned. This holds true even when the property is assigned
undefined
. However, TypeScript doesn't differentiate between a property that is not set and a property that is set toundefined
. This causes problems when you spread an object with a property set toundefined
—TypeScript assumes the property is not set at all, so it skips it and infers from whatever other (earlier in the spread) values the property could take.In the example above,
y
andz
are indifferentiable to TypeScript, but when you spread them the resulting objects are different.xy.a
is in fact a number, as TypeScript thinks it is, because{...x, ...y}
uses the value ofa
fromx
.xz.a
, on the other hand, isundefined
, but TypeScript still assumes it's a number because it thinks{...x, ...z}
used the value ofa
fromx
.🙂 Expected behavior
I'm really not sure how TypeScript could deal with this case best. It could give
a
typenumber | undefined
in both scenarios above, but then that's annoying whena
really is not set on the object (y
in the example).Ideally I think there would be a difference in types between
y
andz
. TypeScript already does this if the type is inferred—if you remove: { a?: number}
from the third line above,x
gets type{ a: undefined }
andxza
gets typeundefined
, which is what it should be. The issue seems to come from the property being explicitly optional but set toundefined
.Additional information about the issue
Closely related to #41418.
The text was updated successfully, but these errors were encountered: