microsoft/TypeScript

Possibility of `undefined` from optional properties is not honoured in spread

OliverJAsh opened this issue · 2 comments

TypeScript Version: 3.6.3

Search Terms: spread optional undefined object properties property

Code

Here's an example of spreading an object with optional properties:

const myQueryDefaults: { a: number; } = { a: 1 };
const myQuery: { a?: number; } = {};

const result = { ...myQueryDefaults, ...myQuery };

// type is `number`
// runtime value is `number`
result.a;

The above example makes sense. However, an optional property does not only mean "this property may not exist" ({})—it also means "this property may exist with a value of undefined" ({ a: undefined }). (Related: #13195.)

As a result of this, the type information may mismatch the actual types at runtime:

const myQueryDefaults: { a: number; } = { a: 1 };
const myQuery: { a?: number;  } = { a: undefined };

const result = { ...myQueryDefaults, ...myQuery };

// type is `number`, but runtime value is `undefined`!!
// expected type: `number | undefined`
result.a;

… which could result in runtime exceptions:

result.a.toString(); // runtime exception, because `result.a` is `undefined`

Ideally optional properties would only mean "this property may not exist" (#13195), in which case the existing behaviour with spreading makes sense. However, in the interim, perhaps the behaviour of spreading should be changed?

Extracted from #13195 (comment)

Also potentially related: #31025

However, in the interim, perhaps the behaviour of spreading should be changed?

That would most likely break every codebase out there.

#13195 is the actionable version of this. This would be an unbelievably large and unergonomic breaking change.