tc39 / proposal-optional-chaining-assignment

`a?.b = c` proposal
https://tc39.es/proposal-optional-chaining-assignment/
MIT License
188 stars 2 forks source link

Optional assignment that creates keys if they dont exist #6

Open lilnasy opened 1 year ago

lilnasy commented 1 year ago

I'm curious if the following pattern is something this proposal will consider within scope.

// An object containing optimized urls for `imageSrc` in various formats and resolutions
const manifest = {}

manifest[imageSrc]              ??= {}
manifest[imageSrc][format]      ??= {}
manifest[imageSrc][format][width] = url

Relevant: lodash's utility:

_.setWith(manifest, '[${imageSrc}][${format}][${width}]', url)
zaygraveyard commented 1 year ago

I don't believe it is, and your example can be one-lined today (if that what you're after) as follows:

// An object containing optimized urls for `imageSrc` in various formats and resolutions
const manifest = {}

((manifest[imageSrc] ??= {})[format] ??= {})[width] = url
ChristianMayer commented 6 months ago

I want to support this proposal and with this issue included.

Use case is an object with an optional value of type object. As a simple example:

var circle = {r: 1, color: {r: 1, g: 1, b:1}};     // might optionally have "meta" of type object
...
function show(c) {
  ...
  c.meta?.showing = true;
}

Right now the line must be instead e.g. (c.meta ??= {}).showing = true - something that's looking very clumsy.

So my enhancement proposal is that a left hand use (i.e. assignment) of an optional chaining does silently create an empty object. So that

object.optional?.property = value;

is a short cut for

(object.optional ??= {}).property = value;
zaygraveyard commented 6 months ago

@ChristianMayer I disagree with your suggestion. As IMHO, it would imply too much magic. But that's just my opinion, which might not matter much 😅.

Patrick-ring-motive commented 6 months ago

This gives me a decent idea for an alternative to the main use case. I'm sure others have found this before but I like it.

(expr1??{}).prop = val

Not as memory efficient but more DRY and readable than

expr1 == null ? undefined : expr1.prop = val
samueldcorbin commented 4 months ago

Creating simple intermediate structure when it doesn't exist is a pretty common JS annoyance, and that was what I was assuming this proposal was at first (that is, I found it while looking to see if there was a proposal to solve that problem).

Using the return from ??= works, but gets pretty unreadable if you're poking a property into something more deeply nested, and doing this with a library usually involves string overhead.

Short-circuiting the assignment when it doesn't exist seems very useful too though.

It's too bad typescript makes use of !. unviable. It'd be nice if you could have something like: outer?.inner?.evenMoreInner?.property = value as the short-circuit assignment outer.inner!.evenMoreInner!.property = value as an assignment that creates intermediate simple objects if necessary

drpicox commented 2 months ago

But the autocreation of fields and objects with ?. is dangerous, and really unexpected. We do not expect that behavior from the right side of the equals, so I would not expect that from the left side. Maybe a different proposal with a different syntax. Btw... I think that !. is used by Typescript to ensure that they are not null, so do not throw a compile error.

Example: https://www.typescriptlang.org/play/?#code/C4TwDgpgBAglC8UDeAoK6oEMD8AuZUAxvgM7ABOAlgHYDmUAvgDQoMooA2EwW+ciSBlhJZqIANwpMAOkwBCaYQRQA5AAtKK8VAD0OqJR6EA9gFswlLiSmzFy9Zu16DPACbGII6saNmLXIA

type A = {
    a?: { c: string };
}:

let a: A = {} as any;
a.a!.c = 'hi'; // it compiles
a.a.c = 'hi'; // it does not compile