Package Exports
- bunshin-clone
Readme
bunshin-clone
High-performance deep clone utility with descriptor support. Supports circular references and complex built-in types.
- ⚡ Fast (no unnecessary overhead)
- ♻️ Deep clone (no structural sharing)
- 🔁 Supports circular references
- 🧠 Handles Map, Set, Array, TypedArray, Date, RegExp, etc.
- 🧩 Optional descriptor preservation
Usage
import bunshinClone from 'bunshin-clone';
const source = { foo: 1, nested: { x: 1 } };
const result = bunshinClone(source);
console.log(result);
// { foo: 1, nested: { x: 1 } }
console.log(result === source); // false
console.log(result.nested === source.nested); // falseAPI
bunshinClone(target)
bunshinClone(target, options)Options
interface BunshinCloneOptions {
preserveDescriptors?: boolean;
strictDescriptors?: boolean;
}preserveDescriptors
false(default): use standard merge (faster, ignores property descriptors)true: preserve property descriptors (getters/setters, etc.)
const source = {};
Object.defineProperty(source, 'x', {
get: () => 42,
enumerable: true,
});
const result = bunshinClone(source, { preserveDescriptors: true });
Object.getOwnPropertyDescriptor(result, 'x')?.get;
// => functionstrictDescriptors
false(default): skip incompatible descriptorstrue: throw if descriptor cannot be merged (e.g. non-configurable or non-writable)
Object.freeze(obj);
bunshinClone(obj, { strictDescriptors: true });
// => may throw TypeErrorSupported Types
bunshin-clone correctly handles:
- Object (plain + prototype preserved)
- Array
- Map
- Set
- Date
- RegExp
- ArrayBuffer
- DataView
- TypedArray (Uint8Array, etc.)
- Error / DOMException
- Blob
- ImageData
- URL
- URLSearchParams
Circular ref
const a: any = { x: 1 };
a.self = a;
const result = bunshinClone(a);
result.self === result; // trueDescriptor Behavior
Default (fast path)
const source = {
get x() {
return 42;
}
};
const result = bunshinClone(source);
result.x; // 42
// getter is NOT preservedpreserveDescriptors: true
const source = {};
Object.defineProperty(source, 'x', {
get: () => 42,
enumerable: true,
});
const result = bunshinClone(source, {
preserveDescriptors: true,
});
Object.getOwnPropertyDescriptor(result, 'x')?.get;
// => preservedUnsupported / Pass-through Types
Some values are returned as-is:
- Function
- WeakMap / WeakSet
- Proxy (not cloned)
- Other non-cloneable host objects
const fn = () => {};
bunshinClone(fn) === fn; // trueDesign Notes
Deep clone (no structural sharing)
Unlike merge utilities, bunshin-clone always produces a new structure:
const source = { a: { b: 1 } };
const result = bunshinClone(source);
result !== source; // true
result.a !== source.a; // trueGetter / Setter behavior
- Default: evaluated and converted to value
- preserveDescriptors: preserved as-is
Descriptor safety
When preserveDescriptors is enabled:
- descriptors are cloned safely
- original object is never mutated
- errors are controlled via
strictDescriptors
Performance
- No proxy / no diffing
- Minimal branching
- Fast path for plain objects and arrays
- Competitive with structuredClone in many cases
Comparison
| Feature | bunshin-clone | structuredClone | lodash.clonedeep |
|---|---|---|---|
| Circular refs | ✅ | ✅ | ✅ |
| Map / Set | ✅ | ✅ | ⚠️ (partial) |
| TypedArray | ✅ | ✅ | ⚠️ (shallow) |
| Descriptor support | ✅ | ❌ | ❌ |
| Functions | pass-through | ❌ (throws) | pass-through |
| Prototype preserved | ✅ | ❌ | ⚠️ |
| Custom control | ✅ | ❌ | ❌ |
| Performance | ⚡ fast | ⚡ fast | 🐢 slower |