All questions

Deep Clone

Premium

Deep Clone

Implement deepClone(value) — return a new value that has the same structure as the input but shares no references with it. Mutating the clone (or anything nested inside it) must never reach back and touch the original. This is the function structuredClone gives you for free in modern runtimes, but the point of the exercise is to build it yourself: handle nested objects and arrays, detect cycles, and decide what to do about types that don't survive a JSON round-trip.

Signature

// Returns a deep, structural copy of `value`. The result shares no object
// references with the input — mutating the result never mutates the input.
function deepClone<T>(value: T): T;

Examples

const original = { name: 'Ada', address: { city: 'London' } };
const copy = deepClone(original);
copy.address.city = 'Paris';
original.address.city; // 'London'  — original is untouched
copy === original;     // false      — top-level reference is new
copy.address === original.address; // false — nested reference is new
// Arrays clone recursively.
const arr = [[1, 2], [3, 4]];
const cloned = deepClone(arr);
cloned[0].push(99);
arr[0]; // [1, 2]  — original inner array is untouched
// Cycles are handled — no stack overflow.
const a = { name: 'a' };
a.self = a;            // a points at itself
const b = deepClone(a);
b.self === b;          // true — cycle is preserved, but to the *clone*
b.self === a;          // false — never references the original
// Primitives come back verbatim.
deepClone(42);         // 42
deepClone('hello');    // 'hello'
deepClone(null);       // null
deepClone(undefined);  // undefined

Notes

  • Recursion is the shape. Walk every property of every object and array; recurse on each value. Primitives are the base case.
  • Cycles must not blow the stack. If the input contains a reference back to itself (directly or indirectly), your function must terminate. Use a WeakMap keyed by seen source object to map each original to its clone.
  • Arrays and plain objects are the supported types. Array.isArray(value) distinguishes the two — they need different containers ([] vs {}) but the same recursive treatment.
  • Only own enumerable properties. Walk the value's own keys with Object.keys — don't copy the prototype chain, don't copy inherited properties.
  • null is its own thing. typeof null === 'object' but null has no properties. Return it verbatim, not {}.
  • Do not use JSON.parse(JSON.stringify(...)). It looks tempting but silently drops undefined, turns Date into a string, throws on cycles, and erases functions and Symbols. See the solution's "Going further" for what a fuller implementation would add.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium