@tienedev/datype
Version:
Modern TypeScript utility library with pragmatic typing and zero dependencies
91 lines (89 loc) • 2.65 kB
JavaScript
/**
* Creates a deep clone of the given value, recursively cloning nested objects and arrays.
* Handles circular references and preserves object types.
*
* @template T - The type of the value to clone
* @param value - The value to clone
* @returns A deep clone of the input value
*
* @example
* ```typescript
* import { cloneDeep } from 'datype';
*
* const original = {
* name: 'John',
* hobbies: ['reading', 'coding'],
* address: { city: 'Paris', zip: '75001' }
* };
*
* const cloned = cloneDeep(original);
* cloned.address.city = 'London';
*
* console.log(original.address.city); // 'Paris' (unchanged)
* console.log(cloned.address.city); // 'London'
* ```
*/
function cloneDeep(value, seen = new WeakMap()) {
// Handle null and undefined
if (value === null || value === undefined) {
return value;
}
// Handle primitive types
if (typeof value !== 'object') {
return value;
}
// Handle circular references
if (seen.has(value)) {
return seen.get(value);
}
// Handle Date objects
if (value instanceof Date) {
return new Date(value.getTime());
}
// Handle RegExp objects
if (value instanceof RegExp) {
return new RegExp(value.source, value.flags);
}
// Handle Arrays
if (Array.isArray(value)) {
const clonedArray = [];
seen.set(value, clonedArray);
for (let i = 0; i < value.length; i++) {
clonedArray[i] = cloneDeep(value[i], seen);
}
return clonedArray;
}
// Handle Set objects
if (value instanceof Set) {
const clonedSet = new Set();
seen.set(value, clonedSet);
for (const item of Array.from(value)) {
clonedSet.add(cloneDeep(item, seen));
}
return clonedSet;
}
// Handle Map objects
if (value instanceof Map) {
const clonedMap = new Map();
seen.set(value, clonedMap);
for (const [key, val] of Array.from(value)) {
clonedMap.set(cloneDeep(key, seen), cloneDeep(val, seen));
}
return clonedMap;
}
// Handle plain objects
if (typeof value === 'object') {
const clonedObject = {};
seen.set(value, clonedObject);
// Copy all enumerable properties (including inherited ones)
for (const key in value) {
if (Object.prototype.hasOwnProperty.call(value, key)) {
clonedObject[key] = cloneDeep(value[key], seen);
}
}
return clonedObject;
}
// For other types (functions, etc.), return as-is
return value;
}
export { cloneDeep };