fix(core): updating deepCopy to avoid max callstack with circular deps (#4468)

* fix(core): updating deepCopy to avoid max callstack in case of circular dep

* fix(core): show warning with path added to circular reference
This commit is contained in:
Csaba Tuncsik 2022-10-28 15:25:44 +02:00 committed by GitHub
parent 7620d93eda
commit ca60b0e203
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 46 additions and 5 deletions

View file

@ -1,12 +1,16 @@
/* eslint-disable @typescript-eslint/no-explicit-any, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-argument */
export const deepCopy = <T>(source: T): T => {
export const deepCopy = <T>(source: T, hash = new WeakMap(), path = ''): T => {
let clone: any;
let i: any;
const hasOwnProp = Object.prototype.hasOwnProperty.bind(source);
// Primitives & Null
if (typeof source !== 'object' || source === null) {
// Primitives & Null & Function
if (typeof source !== 'object' || source === null || source instanceof Function) {
return source;
}
if (hash.has(source)) {
console.warn(`Circular reference detected at "source${path}"`);
return hash.get(source);
}
// Date
if (source instanceof Date) {
return new Date(source.getTime()) as T;
@ -16,15 +20,16 @@ export const deepCopy = <T>(source: T): T => {
clone = [];
const len = source.length;
for (i = 0; i < len; i++) {
clone[i] = deepCopy(source[i]);
clone[i] = deepCopy(source[i], hash, path + `[${i as string}]`);
}
return clone;
}
// Object
clone = {};
hash.set(source, clone);
for (i in source) {
if (hasOwnProp(i)) {
clone[i] = deepCopy((source as any)[i]);
clone[i] = deepCopy((source as any)[i], hash, path + `.${i as string}`);
}
}
return clone;

View file

@ -48,4 +48,40 @@ describe('deepCopy', () => {
expect(copy.deep.props).toEqual(object.deep.props);
expect(copy.deep.props).not.toBe(object.deep.props);
});
it('should avoid max call stack in case of circular deps', () => {
const object: Record<string, any> = {
deep: {
props: {
list: [{ a: 1 }, { b: 2 }, { c: 3 }],
},
arr: [1, 2, 3],
},
arr: [
{
prop: {
list: ['a', 'b', 'c'],
},
},
],
func: () => {},
date: new Date(),
undef: undefined,
nil: null,
bool: true,
num: 1,
};
object.circular = object;
object.deep.props.circular = object;
object.deep.arr.push(object)
const copy = deepCopy(object);
expect(copy).toEqual(object);
expect(copy).not.toBe(object);
expect(copy.arr).toEqual(object.arr);
expect(copy.arr).not.toBe(object.arr);
expect(copy.deep.props).toEqual(object.deep.props);
expect(copy.deep.props).not.toBe(object.deep.props);
});
});