UNPKG

metarize

Version:

A lightweight, ESM-compatible TypeScript metadata library for creating and inspecting decorators with zero dependencies

121 lines (105 loc) 3.74 kB
/** * A collection of types that can be cloned */ export const cloneableTypes = new Set<Function>([ Object, Array, Set, Map, RegExp, Date, Buffer, ArrayBuffer, Float32Array, Float64Array, Int8Array, Int16Array, Int32Array, Uint8Array, Uint8ClampedArray, Uint16Array, Uint32Array, ]); /** * Deep clone a value * @param val - The value to clone * @param refs - Map of references to handle circular references (internal use) * @returns A deep clone of the value */ export function cloneDeep<V>(val: Readonly<V>, refs = new WeakMap<object, any>()): V { if (val === null || typeof val !== 'object') return val; // Handle circular references if (refs.has(val as object)) { throw new Error('Circular reference detected during deep cloning'); } // Handle special types that should not be deep cloned if (val.constructor != null && !cloneableTypes.has(val.constructor)) { // Do not clone instances of classes/constructors, such as custom classes return val; } // Add this object to the refs map refs.set(val as object, true); // Handle arrays if (Array.isArray(val)) { const result = val.map(item => cloneDeep(item, refs)) as unknown as V; refs.delete(val as object); // Clean up refs map return result; } // Handle Set if (val instanceof Set) { const newSet = new Set(); val.forEach(item => newSet.add(cloneDeep(item, refs))); refs.delete(val as object); // Clean up refs map return newSet as unknown as V; } // Handle Map if (val instanceof Map) { const newMap = new Map(); val.forEach((value, key) => newMap.set(key, cloneDeep(value, refs))); refs.delete(val as object); // Clean up refs map return newMap as unknown as V; } // Handle RegExp if (val instanceof RegExp) { refs.delete(val as object); // Clean up refs map return new RegExp(val.source, val.flags) as unknown as V; } // Handle Date if (val instanceof Date) { refs.delete(val as object); // Clean up refs map return new Date(val.getTime()) as unknown as V; } // Handle ArrayBuffer and typed arrays if (val instanceof ArrayBuffer) { const newBuffer = new ArrayBuffer(val.byteLength); new Uint8Array(newBuffer).set(new Uint8Array(val)); refs.delete(val as object); // Clean up refs map return newBuffer as unknown as V; } if (ArrayBuffer.isView(val) && !(val instanceof DataView)) { // Handle typed arrays (Float32Array, Uint8Array, etc.) // Use any typed array method that creates a copy refs.delete(val as object); // Clean up refs map if (val instanceof Float32Array) return new Float32Array(val) as unknown as V; if (val instanceof Float64Array) return new Float64Array(val) as unknown as V; if (val instanceof Int8Array) return new Int8Array(val) as unknown as V; if (val instanceof Int16Array) return new Int16Array(val) as unknown as V; if (val instanceof Int32Array) return new Int32Array(val) as unknown as V; if (val instanceof Uint8Array) return new Uint8Array(val) as unknown as V; if (val instanceof Uint8ClampedArray) return new Uint8ClampedArray(val) as unknown as V; if (val instanceof Uint16Array) return new Uint16Array(val) as unknown as V; if (val instanceof Uint32Array) return new Uint32Array(val) as unknown as V; // Fallback for any other ArrayBufferView types return val as V; } // Handle plain objects const newObj: Record<string, any> = {}; for (const key in val) { if (Object.hasOwn(val, key)) { newObj[key] = cloneDeep((val as Record<string, any>)[key], refs); } } refs.delete(val as object); // Clean up refs map return newObj as V; } // 这里可以添加其他工具函数