microsoft/TypeScript

Prevent undefined assignments to optional values

pygy opened this issue · 6 comments

pygy commented

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>[]): T

The 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)

Duplicate of #13195, which unfortunately gets no love.

I wouldn't say it's a duplicate. One seems to seek to change the behavior of an existing feature. This one seeks to introduce new syntax because of an existing feature.

I am against changing the behavior of T? because it actually makes sense to me for it to have undefined automatically added to the type.

But can see how it's useful to say an optional field, if set, cannot be undefined.

There's no way we would do this feature (which has very limited applicability) instead of #13195 (which solves this problem + others at the same time), so taking it as a duplicate.

pygy commented

I'd argue this as the same applicability as #13195, and it has the advantage of not being a breaking change, which means it could land sooner (with the recent semver major bump, I assume the next one will take some time).

Edit: I'm probably missing something... what would #13195 solve that this wouldn't?

This issue has been marked as a 'Duplicate' and has seen no recent activity. It has been automatically closed for house-keeping purposes.

pygy commented

@RyanCavanaugh This issue basically expired. I still disagree that it has less applicability than #13195.

It introduces the same functionality in a way that is backwards compatible necessitating only a semver minor update.

Even if #13195 landed as a distinct compiler option, thus technically not BW/incompatible, it would not be possible to mix old code written with the the old ? behaviour and newer code that expects the new one.

Could you re-assess, and possibly re-open?