deepFreeze
Recursively freezes an object and all of its nested properties. Makes the entire structure immutable.
1/**
2 * Recursively freezes an object and all of its nested properties.
3 * Makes the entire structure immutable.
4 *
5 * @param obj - The object to deeply freeze.
6 * @returns The same object, deeply frozen.
7 */
8export function deepFreeze<T>(obj: T): T {
9 if (obj === null || typeof obj !== 'object' || Object.isFrozen(obj)) {
10 return obj;
11 }
12
13 Object.freeze(obj);
14
15 const propNames = [
16 ...Object.getOwnPropertyNames(obj),
17 ...Object.getOwnPropertySymbols(obj) as (string | symbol)[],
18 ];
19
20 for (const prop of propNames) {
21 const value = (obj as any)[prop];
22 if (
23 value !== null &&
24 typeof value === 'object' &&
25 !Object.isFrozen(value)
26 ) {
27 deepFreeze(value);
28 }
29 }
30
31 return obj;
32}
Full Recursive Immutability
Freezes not only the top-level object but all nested properties, ensuring deep immutability.
Supports Symbols and String Keys
Handles both string and symbol property names, covering advanced object structures.
Safe Against Re-Freezing
Skips properties that are already frozen, improving performance and preventing redundant processing.
Type-Preserving Output
Returns the same object with unchanged structure and typing, making it easy to integrate without casting.
Null-Safe and Guarded
Gracefully exits on
null
or non-object inputs, avoiding runtime errors.
Tests | Examples
1test('deepFreeze - makes top-level object immutable', () => {
2 const obj = { a: 1 };
3 const frozen = deepFreeze(obj);
4 expect(Object.isFrozen(frozen)).toBe(true);
5 expect(() => { (frozen as any).a = 2 }).not.toThrow(); // silently fails in non-strict mode
6 expect(frozen.a).toBe(1);
7});
8
9test('deepFreeze - freezes nested objects', () => {
10 const obj = { a: { b: 2 } };
11 const frozen = deepFreeze(obj);
12 expect(Object.isFrozen(frozen.a)).toBe(true);
13 expect(() => { (frozen.a as any).b = 5 }).not.toThrow();
14 expect(frozen.a.b).toBe(2);
15});
16
17test('deepFreeze - handles arrays', () => {
18 const obj = { list: [1, 2, 3] };
19 const frozen = deepFreeze(obj);
20 expect(Object.isFrozen(frozen.list)).toBe(true);
21 expect(() => { (frozen.list as any)[0] = 9 }).not.toThrow();
22 expect(frozen.list[0]).toBe(1);
23});
24
25test('deepFreeze - handles symbols and ignores non-objects', () => {
26 const sym = Symbol('x');
27 const obj: any = { [sym]: { deep: true }, prim: 42 };
28 const frozen = deepFreeze(obj);
29 expect(Object.isFrozen(frozen[sym])).toBe(true);
30 expect(frozen.prim).toBe(42);
31});
32
33test('deepFreeze - returns primitives unchanged', () => {
34 expect(deepFreeze(42)).toBe(42);
35 expect(deepFreeze(null)).toBe(null);
36 expect(deepFreeze(undefined)).toBe(undefined);
37});
Common Use Cases
Immutable State Enforcement
Prevent accidental mutation of application state in frameworks like Redux or MobX.
Secure Configuration Objects
Lock down configuration structures to avoid runtime changes or side effects.
Library API Contracts
Expose objects that cannot be mutated by consumers of a library or module.
Testing and Debugging
Detect unintended mutations during test execution by freezing expected data structures.
Snapshot Preservation
Protect in-memory snapshots of data from being altered across logic layers or async operations.