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:
// Only null-proto objects can become records
Object.freezeRecord({}); // throws a TypeError
Object.freezeRecord(Object.create(null)); // OK
Using records looks like this:
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:
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.