mirror of
https://github.com/trezor/trezor-suite.git
synced 2026-03-02 21:45:14 +01:00
feat(utils): cloneCyclcicObject new util
This commit is contained in:
committed by
martin
parent
5253a9daec
commit
a65cc86086
37
packages/utils/src/cloneObjectCyclic.ts
Normal file
37
packages/utils/src/cloneObjectCyclic.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
// Makes a deep copy of an object, handling cyclical references.
|
||||
export const cloneObjectCyclic = <T>(obj: T, seen = new WeakMap<object, any>()): T => {
|
||||
if (obj === null || typeof obj !== 'object') {
|
||||
return obj;
|
||||
}
|
||||
|
||||
if (seen.has(obj)) {
|
||||
return seen.get(obj);
|
||||
}
|
||||
|
||||
if (obj instanceof ArrayBuffer) {
|
||||
return obj.slice(0) as any;
|
||||
}
|
||||
|
||||
if (ArrayBuffer.isView(obj)) {
|
||||
const TypedArrayConstructor = obj.constructor as new (...args: any[]) => typeof obj;
|
||||
|
||||
return new TypedArrayConstructor(obj) as any;
|
||||
}
|
||||
|
||||
const clone: any = Array.isArray(obj) ? [] : {};
|
||||
seen.set(obj, clone);
|
||||
|
||||
for (const key in obj) {
|
||||
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
||||
const value = (obj as any)[key];
|
||||
|
||||
if (typeof value === 'function' || typeof value === 'symbol') {
|
||||
continue;
|
||||
}
|
||||
|
||||
(clone as any)[key] = cloneObjectCyclic(value, seen);
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
};
|
||||
@@ -49,3 +49,4 @@ export * from './throttler';
|
||||
export * from './extractUrlsFromText';
|
||||
export * from './isFullPath';
|
||||
export * from './asciiUtils';
|
||||
export * from './cloneObjectCyclic';
|
||||
|
||||
71
packages/utils/tests/cloneObjectCyclic.test.ts
Normal file
71
packages/utils/tests/cloneObjectCyclic.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { cloneObjectCyclic } from '../src/cloneObjectCyclic';
|
||||
|
||||
describe('cloneObjectCyclic', () => {
|
||||
describe('deep cloning of objects', () => {
|
||||
it('should clone a simple object', () => {
|
||||
const original = { a: 1, b: 2 };
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
});
|
||||
|
||||
it('should clone an object with nested properties', () => {
|
||||
const original = { a: { b: { c: 3 } } };
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned.a).not.toBe(original.a);
|
||||
});
|
||||
|
||||
it('should handle cyclical references', () => {
|
||||
const original = { a: 1 };
|
||||
// @ts-expect-error"
|
||||
original['cyclical'] = original;
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).toEqual(original);
|
||||
// @ts-expect-error"
|
||||
expect(cloned['cyclical']).toBe(cloned);
|
||||
});
|
||||
|
||||
it('should clone arrays', () => {
|
||||
const original = [1, 2, 3, { a: 4 }];
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned[3]).not.toBe(original[3]);
|
||||
});
|
||||
|
||||
it('should handle arrays with cyclical references', () => {
|
||||
const original = [1, 2, 3];
|
||||
// @ts-expect-error"
|
||||
original.push(original);
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).toEqual(original);
|
||||
expect(cloned[3]).toBe(cloned);
|
||||
});
|
||||
|
||||
it('should clone an object with functions and symbols', () => {
|
||||
const original = {
|
||||
a: 1,
|
||||
b: () => {},
|
||||
c: Symbol('symbol'),
|
||||
};
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).toEqual({ a: 1 });
|
||||
expect(cloned).not.toBe(original);
|
||||
});
|
||||
|
||||
it('should clone an ArrayBuffer', () => {
|
||||
const original = new ArrayBuffer(8);
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned.byteLength).toBe(original.byteLength);
|
||||
});
|
||||
|
||||
it('should clone typed arrays', () => {
|
||||
const original = new Uint8Array([1, 2, 3]);
|
||||
const cloned = cloneObjectCyclic(original);
|
||||
expect(cloned).not.toBe(original);
|
||||
expect(cloned).toEqual(original);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user