All files clone.ts

98.3% Statements 58/59
96.96% Branches 32/33
100% Functions 4/4
100% Lines 52/52

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90  181x     3x 94x   45x   44x 44x   43x 1x     42x 1x     41x 1x 1x 1x 1x   1x     40x 1x 1x 1x 1x   1x     39x 2x 2x 2x 2x     37x 37x   37x 53x 53x   53x 52x     53x     37x     3x 87x 41x   40x   40x 1x 1x 1x   39x 1x 1x     38x 51x       40x     3x 31x    
function isObjectLike(value: unknown): value is object {
  return (typeof value === 'object' && value !== null) || typeof value === 'function';
}
 
export function cloneDeep<T>(value: T, seen = new WeakMap<object, unknown>()): T {
  if (!isObjectLike(value)) return value;
 
  if (typeof value === 'function') return value;
 
  const existing = seen.get(value);
  if (existing !== undefined) return existing as T;
 
  if (value instanceof Date) {
    return new Date(value.getTime()) as T;
  }
 
  if (value instanceof RegExp) {
    return new RegExp(value.source, value.flags) as T;
  }
 
  if (value instanceof Map) {
    const cloned = new Map<unknown, unknown>();
    seen.set(value, cloned);
    for (const [key, mapValue] of value.entries()) {
      cloned.set(cloneDeep(key, seen), cloneDeep(mapValue, seen));
    }
    return cloned as T;
  }
 
  if (value instanceof Set) {
    const cloned = new Set<unknown>();
    seen.set(value, cloned);
    for (const setValue of value.values()) {
      cloned.add(cloneDeep(setValue, seen));
    }
    return cloned as T;
  }
 
  if (Array.isArray(value)) {
    const cloned: unknown[] = [];
    seen.set(value, cloned);
    for (const item of value) cloned.push(cloneDeep(item, seen));
    return cloned as T;
  }
 
  const cloned = Object.create(Object.getPrototypeOf(value)) as Record<PropertyKey, unknown>;
  seen.set(value, cloned);
 
  for (const key of Reflect.ownKeys(value)) {
    const descriptor = Object.getOwnPropertyDescriptor(value, key);
    Iif (descriptor === undefined) continue;
 
    if ('value' in descriptor) {
      descriptor.value = cloneDeep(descriptor.value, seen);
    }
 
    Object.defineProperty(cloned, key, descriptor);
  }
 
  return cloned as T;
}
 
export function deepFreeze<T>(value: T, seen = new WeakSet<object>()): T {
  if (!isObjectLike(value)) return value;
  if (seen.has(value)) return value;
 
  seen.add(value);
 
  if (value instanceof Map) {
    for (const [key, mapValue] of value.entries()) {
      deepFreeze(key, seen);
      deepFreeze(mapValue, seen);
    }
  } else if (value instanceof Set) {
    for (const setValue of value.values()) {
      deepFreeze(setValue, seen);
    }
  } else {
    for (const key of Reflect.ownKeys(value)) {
      deepFreeze((value as Record<PropertyKey, unknown>)[key], seen);
    }
  }
 
  return Object.freeze(value);
}
 
export function immutableClone<T>(value: T): T {
  return deepFreeze(cloneDeep(value));
}