Skip to content

Instantly share code, notes, and snippets.

@jcorbin
Last active November 1, 2021 22:17
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcorbin/8f7ed25df4ac9ae6fa4dc0819314083f to your computer and use it in GitHub Desktop.
Save jcorbin/8f7ed25df4ac9ae6fa4dc0819314083f to your computer and use it in GitHub Desktop.
import test from 'ava';
/**
* deep object freeze that avoids the Readonly<{set foo()}> trap, and avoids
* needing to pull in something heavier like harden()
*
* @template {object} O
* @param {O} o
* @returns {O}
*/
function freeze(o) {
// freeze the object first to break any cycles if we re-encounter this
// object in the loop below (or any recursive sub-loop thereof)
Object.freeze(o);
for (const [_key, {value}] of Object.entries(Object.getOwnPropertyDescriptors(o))) {
// skip accessor properties
if (value === undefined) continue;
// skip primitives
if (typeof value !== 'object') continue;
// skip already frozen objects
if (Object.isFrozen(value)) continue;
freeze(value);
}
// NOTE: this intentionally discards the Readonly<T> wrapper
return o;
}
test('freeze', t => {
let cv = 7;
const o = {
a: 3,
bs: [4, 5, 6],
get c() { return cv },
set c(v) { cv = v },
/** @type {any} this field is just a hack to prove cycle safety */
d: null,
};
o.d = o;
freeze(o);
// nope to regular property change
t.is(o.a, 3);
t.throws(() => { o.a = 9 }, {message: /Cannot assign to read only property/});
t.is(o.a, 3);
// nope to array element change, frozen
t.is(o.bs[1], 5);
t.throws(() => { o.bs[1] = 9 }, {message: /Cannot assign to read only property/});
t.is(o.bs[1], 5);
// nope to array push, frozen
t.deepEqual(o.bs, [4, 5, 6]);
t.throws(() => o.bs.push(9), {message: /Cannot add property 3/})
t.deepEqual(o.bs, [4, 5, 6]);
// nope to array pop, frozen
t.deepEqual(o.bs, [4, 5, 6]);
t.throws(() => o.bs.pop(), {message: /Cannot delete property '2'/})
t.deepEqual(o.bs, [4, 5, 6]);
// explicitly defined property change is okay tho
t.is(o.c, 7);
o.c = 9;
t.is(o.c, 9);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment