Propagate frozen state on object spread

Right now if you clone frozen object with spread operator the resulting object will not be frozen.

const a = Object.freeze({x:1});
const b = {...a, y: 1};  // not frozen!
const b1 = Object.freeze({...a, y: 1});  // too verbose!

I propose adding {propagate: boolean} configuration property/parameter to Object.freeze that will make the frozen state automatically propagated with spread operators.

const a = Object.freeze({x: 1}, {propagate: true});
const b = {...a, y: 2}; // frozen!

Q: How is this better than GitHub - tc39/proposal-record-tuple: ECMAScript proposal for the Record and Tuple value types. | S ?
A: It's not, it's complementary. Record tuple, while cleaner and a better solution to immutability, introduces new syntax which will require updating all the code and dependent libraries.

In comparison, Object.freeze can be used with existing syntax. Most well written applications have a lot/most objects that are effectively immutable but it is not enforced anywhere because it is too cumbersome at the moment (unless you reach out for 3rd party libs and even then they won't help with standard spread operator).

With this proposal you can simply freeze objects in single place (e.g. after deserializing) and have it enforced throughout your entire program. With little to no code changes.

For maximum effectiveness should be combined with deep freezing (ideally available as another configuration property of Object.freeze).

What would these do:

const a = Object.freeze({x: 1}, {propagate: true});
const b = {x: 2};
const cc = {...a, ...b};
const dd = {...b, ...a};

How would i then spread a frozen object with propagation into a mutable one?

1 Like

Any spread on such frozen object makes the result frozen. Both cc and dd are frozen.

Without using spread, by copying properties: Object.assign({}. somethingFrozen)

I suspect that wouldn't be web-compatible :-/

1 Like

Object spread syntax is sugar for Object.assign and must remain so, so changing spread in this way would have to change Object.assign as well.

1 Like

This could also cause unexpected breaking changes to libraries. For example:

function omit(obj_, keys) {
  const obj = { ...obj_ }
  for (const key of keys) {
    delete obj[key]
  return obj

Such a function will properly work with both frozen and non-frozen objects. But, it will cause some really weird and unexpected behaviors for objects that propagate their frozeness.

I think what's really needed here is just a shorthand for freezing objects, so that the Object.freeze({ ...x, y: 2 }) case isn't so verbose. This is exactly what this proposal seeks to provide.

1 Like

No it won't, not to that degree anyways - that's an explicit anti-goal for that proposal. You could still even use Lodash's _.get freely with them without issue.