Yevhen Klymentiev
dark
light
console
darkness
y.klymentiev@gmail.com
Reusable Snippets|Practical utility code for everyday use — custom-built and ready to share

deepFreeze

Recursively freezes an object and all of its nested properties. Makes the entire structure immutable.

TypeScript
Copied!
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

TypeScript
Copied!
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.

Codebase: Utilities -> Objects -> deepFreeze | Yevhen Klymentiev