-
Notifications
You must be signed in to change notification settings - Fork 13.2k
Description
Search Terms
mapped optional undefined
Suggestion
I would like a way to have optional object fields that can't be assigned undefined. Getting that value may yield undefined if the field is absent, but it should not be possible to set it.
Use Cases
I'm in the process of writing typings for the Patchinko JS library that allows one to patch objects like a bit like Object.assign, but also supports efficient deep patching (left out of the examples for clarity):
const patched = patch({a: 5, b: 6}, {a: 4})Where patch is defined as
const Delete = Symbol()
type Patch<T> = {
[P in keyof T]?: T[P] | (undefined extends T[P] ? typeof Delete : never)
}
declare function patch<T>(x: T, ...rest: Patch<T>[]): TThe problem here is that Patch<T> lets one define objects that have undefined values, and patch({a: 5}, {}) doesn't behave like patch({a: 5}, {a: undefined}).
Sugested syntax: allow ?! where ? is legal.
Note that this would still let one delete fields defined as mandatory but have undefined as a valid type. It would be nice to also have a predicate to tell apart optional fields from "nullable" ones.
Examples
Pseudo-TS:
const Delete = Symbol()
type Patch<T> = {
[P in keyof T] ?! : T[P] | (undefined extends T[P] ? typeof Delete : never)
}
function patch<T>(source: T, ...patches: Patch<T>[]): T {
for (const patch of patches) {
for (const k in patch) {
if (patch[k] === Delete) delete source[k]
// currently a type error if you replace the with a ?! with a ?
else source[k] = patch[k]
}
}
return source
}
interface X {
a: number,
b?: number
}
const o = {a: 5} as X
const p = patch(o, {b: 6})
const q = patch(p, {b: Delete})
const r = patch (q, {a: undefined}) // this should be a type error.Checklist
My suggestion meets these guidelines:
- This wouldn't be a breaking change in existing TypeScript / JavaScript code
- This wouldn't change the runtime behavior of existing JavaScript code
- This could be implemented without emitting different JS based on the types of the expressions
- This isn't a runtime feature (e.g. new expression-level syntax)