From b1553a23f82f2416543f710518e6eb35774653f9 Mon Sep 17 00:00:00 2001 From: Tim Griesser Date: Sat, 18 Mar 2023 09:06:01 -0400 Subject: [PATCH] fix: handle cloning proxied classes w/ enumerable getters --- packages/utils/src/helpers.ts | 8 ++++++-- test/core/test/utils.spec.ts | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/packages/utils/src/helpers.ts b/packages/utils/src/helpers.ts index ed1636fc148c..961a8274e11c 100644 --- a/packages/utils/src/helpers.ts +++ b/packages/utils/src/helpers.ts @@ -86,8 +86,12 @@ export function clone(val: T, seen: WeakMap): T { seen.set(val, out) // we don't need properties from prototype const props = getOwnProperties(val) - for (const k of props) - out[k] = clone((val as any)[k], seen) + for (const k of props) { + Object.defineProperty(out, k, { + ...Object.getOwnPropertyDescriptor(val, k), + value: clone((val as any)[k], seen), + }) + } return out } diff --git a/test/core/test/utils.spec.ts b/test/core/test/utils.spec.ts index 5e9fdc5a6251..865865cbfb15 100644 --- a/test/core/test/utils.spec.ts +++ b/test/core/test/utils.spec.ts @@ -149,6 +149,41 @@ describe('deepClone', () => { objD.ref = objD expect(deepClone(objD)).toEqual(objD) }) + + test('can clone classes with proxied enumerable getters', () => { + const obj = Symbol.for('aClass') + class A { + [obj]: { a: number; b: string } + constructor(data: { a: number; b: string }) { + this[obj] = data + return new Proxy(this, { + ownKeys() { + return Reflect.ownKeys(data) + }, + getOwnPropertyDescriptor(target, p) { + return { + ...Reflect.getOwnPropertyDescriptor(data, p), + enumerable: true, + writable: false, + } + }, + }) + } + + get a() { + return this[obj].a + } + + get b() { + return this[obj].b + } + } + const aClass = new A({ a: 1, b: 'B' }) + expect(aClass.a).toEqual(1) + expect(aClass.b).toEqual('B') + expect(Object.keys(aClass)).toEqual(['a', 'b']) + expect(deepClone({ aClass })).toEqual({ aClass: new A({ a: 1, b: 'B' }) }) + }) }) describe('resetModules doesn\'t resets only user modules', () => {