# Object.freezeRecord

**URL:** <https://es.discourse.group/t/object-freezerecord/2543>\
**Category:** 💡 Ideas\
**Created:** [April 18, 2026, 10:29pm UTC](https://es.discourse.group/t/object-freezerecord/2543 "2026-04-18T22:29:21Z")\
**Posts on this page:** 4\
**Page:** 1

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 18, 2026, 10:29pm UTC](https://es.discourse.group/t/object-freezerecord/2543/1 "2026-04-18T22:29:21Z")

</div>

This is part of a continuing attempt to figure out how to handle immutable data in Javascript.

My goal is to create some kind of deep validation function that can be used at a function boundary so that inside the function, property access like `validatedObj.foo` or `validatedObj[0]` are known to be safe, or as safe as possible in a world with proxies.

This idea supplants my own earlier `Object.deepFreeze` idea:

```javascript
// Only null-proto objects can become records
Object.freezeRecord({}); // throws a TypeError

Object.freezeRecord(Object.create(null)); // OK

```

Using records looks like this:

```javascript
function doubleValue(obj) => {
  if (!Object.isRecord(obj)); throw new Error();

  return obj.value + obj.value;
}

```

To keep things dirt simple for the implementation, `freezeRecord` only ever freezes one object at a time. It's not deeply freezing, but it _is_ doing deep validation:

```javascript
let { assign, create: createObject, freezeRecord } = Object;

let plainObj = obj => assign(createObject(null), obj);

let record = freezeRecord(plainObj({}));

// records nest within records
freezeRecord(plainObj({ foo: record })); // OK

// no non-record, non-primitve values
freezeRecord(plainObj({ foo: createObject(null) })); // TypeError

// no getters
freezeRecord(plainObj({ get foo() {} })); // throws a TypeError

```

It would be absolutely critical to pair this with some performant engine-supported way of making null-prototype arrays. I would recommend the API be `Object.createArray(null)`. Without `createArray` (or something like it) this whole thing is a pipe-dream from the perf standpoint. Right now you have to use the deathly-slow `setPrototypeOf` to make an array with a null prototype.

The deep guarantee of prototypeless-ness ensures that all internal uses of `.` property access are free from prototype pollution. The guarantee against getters and un-frozen properties means the data in the record is immutable. Our `doubleValue` function should always return the correct result!

In terms of how well it would integrate with other language features, I think it's pretty tame. There might be some overlap with proposal-composites, but even out of the box they'd play nicely enough together. Records could probably be zero-copy transferred using `structuredClone` too which'd be great.

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 18, 2026, 11:03pm UTC](https://es.discourse.group/t/object-freezerecord/2543/2 "2026-04-18T23:03:45Z")

</div>

@aclaymore Both the most closely related proposals, `proposal-record-tuple` and `proposal-composites`, are yours. I guess that means I'm especially interested in your feedback!

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 19, 2026, 1:31pm UTC](https://es.discourse.group/t/object-freezerecord/2543/3 "2026-04-19T13:31:22Z")

</div>

Eventually I think you'd also take the proposed record syntax from `proposal-record-tuple` and make it sugar for calls to `freezeRecord` so that you could just write this:

```javascript
function doubleValue(obj) => {
  if (!Object.isRecord(obj)); throw new Error();

  return obj.value + obj.value;
}

doubleValue(#{ value: 2 }); // 4

```

If I understand, the benefit this has over the previous records proposal is that it's much more well-defined how records interact with other types. The records proposal was tricky in that way because it made `typeof record === 'record'` which would have caused existing object-based metaprogramming to go haywire

---

<div class="post-metadata">

**Author:** ![conartist6](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/conartist6/32/515_2.png) [@conartist6](https://es.discourse.group/u/conartist6)\
**Post date:** [April 19, 2026, 2:19pm UTC](https://es.discourse.group/t/object-freezerecord/2543/4 "2026-04-19T14:19:10Z")

</div>

The biggest practical difficulty this proposal faces is the impracticality of making null-prototype arrays in any current runtime. For what it costs to use `setPrototypeOf` you may as well not be able to do it at all, which would mean that while this whole proposal can be polyfilled, it cannot be polyfilled with acceptable performance as it has been stated here : (

I see two potential ways to go about this:

1. Rush towards support for a "low-level" method like `Object.createArray(null)`, and lean on the poor-performing polyfill where we must. We know this can be done because it has been done in the past with primitives like `Map` and `WeakMap` which started out with poor-performance polyfills (and indeed experienced very slow initial adoption)
2. Allow records to be plain objects (with `Object.prototype` or `Array.prototype`) and introduce a `#.` "own property access" operator. Such an operator could be used with any object and would never access properties from the prototype chain.

I think solution 1 is the better design. It's closer to the ideal. JS authors don't have to remember to use `#.` to stay safe when working with records. Use records and you can't get hurt. Instead of another nitpick thing to remember to do stay safe, coders get an actual strong shield that protects everything behind it.

The problem with solution 1 is that it's a hardish-break in compatibility because of the poor-performing polyfill. If I go out and write a Figma clone that uses Records, it isn't going to run well on any runtime that doesn't have native support for `Object.createArray(null)`, which is every current runtime. So basically it could be... years... before devs could really architect around this feature and expect it to run well for users everywhere.

By comparison polyfilling `record#.property` is dirt cheap because there's no `setPrototypeOf` needed. You can just desugar to an intermediate step with a `hasOwn` check. The `#.` would tend to proliferate in data-handling code, but it's useful even if you aren't using records and I would think that it would allow the runtime to implement the operation with better performance too: checking the prototype chain isn't free.
