Object.freeze / Object.seal
debt(d5/e3/b3/t7)
Closest to 'specialist tool catches it' (d5). TypeScript can warn about reassignment to frozen objects, but the canonical misconception—shallow vs. deep freezing—requires runtime testing or code review to catch. Most linters don't flag freeze misuse by default.
Closest to 'simple parameterised fix' (e3). The quick_fix explicitly recommends swapping to deepFreeze() or using TypeScript readonly—a one-component refactor. Replacing Object.freeze() calls with a safer pattern is straightforward and localized.
Closest to 'localised tax' (b3). Object.freeze() decisions affect only the objects being frozen and their call sites. It doesn't shape system architecture or create cross-cutting obligations like shared mutable state would. The burden is confined to the immutability strategy of individual data structures.
Closest to 'serious trap' (t7). The misconception directly contradicts developer intuition: freeze() *sounds* like it makes the whole object immutable, but it only freezes the top level. Nested objects remain mutable—a documented gotcha that contradicts the obvious expectation. Additionally, silent failure in non-strict mode adds a second serious trap.
TL;DR
Explanation
Object.freeze(obj): no new properties, no changes, no deletions. Silently fails in non-strict mode, throws TypeError in strict mode. Object.isFrozen() checks. Object.seal(obj): no new/deleted properties, existing writable properties can change. Readonly check with Object.isSealed(). Both are SHALLOW — nested objects remain mutable. Deep freeze requires recursion. Use cases: constants, config objects, action type maps in Redux, enums before JS had them. For deep immutability: recursively freeze, use Immer, or use TypeScript readonly.
Common Misconception
Why It Matters
Common Mistakes
- Assuming freeze is deep — nested objects still mutable.
- Not knowing freeze silently fails in non-strict mode.
- Using freeze on large objects in hot paths — slight performance cost.
Code Examples
const CONFIG = { db: { host: 'localhost', port: 5432 } };
Object.freeze(CONFIG);
CONFIG.db.port = 9999; // Works! db is not frozen
function deepFreeze(obj) {
Object.getOwnPropertyNames(obj).forEach(name => {
const val = obj[name];
if (val && typeof val === 'object') deepFreeze(val);
});
return Object.freeze(obj);
}
const CONFIG = deepFreeze({ db: { host: 'localhost', port: 5432 } });
CONFIG.db.port = 9999; // TypeError in strict mode