From 0ad433e67be8d4aae7e008a936f8c5bdd1fa66fd Mon Sep 17 00:00:00 2001 From: rory-instil <70631281+rory-instil@users.noreply.github.com> Date: Tue, 22 Aug 2023 15:50:57 +0100 Subject: [PATCH] fix: prototype methods being discarded when using `setData` (#2166) Currently, when calling setData on a wrapper it will not retain methods on any constructed objects passed in. These methods reside in the prototype which the current logic does not persist across. These changes will ensure that any prototype properties/methods are copied over to the proxied data object. fixes #1851 --- src/utils.ts | 30 ++++++++++++++++-------------- tests/setData.spec.ts | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 14 deletions(-) diff --git a/src/utils.ts b/src/utils.ts index 8900af7bf..7b65a7ec7 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -80,20 +80,22 @@ export const mergeDeep = ( if (!isObject(target) || !isObject(source)) { return source } - Object.keys(source).forEach((key) => { - const targetValue = target[key] - const sourceValue = source[key] - - if (Array.isArray(targetValue) && Array.isArray(sourceValue)) { - target[key] = sourceValue - } else if (sourceValue instanceof Date) { - target[key] = sourceValue - } else if (isObject(targetValue) && isObject(sourceValue)) { - target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue) - } else { - target[key] = sourceValue - } - }) + Object.keys(source) + .concat(Object.getOwnPropertyNames(Object.getPrototypeOf(source) ?? {})) + .forEach((key) => { + const targetValue = target[key] + const sourceValue = source[key] + + if (Array.isArray(targetValue) && Array.isArray(sourceValue)) { + target[key] = sourceValue + } else if (sourceValue instanceof Date) { + target[key] = sourceValue + } else if (isObject(targetValue) && isObject(sourceValue)) { + target[key] = mergeDeep(Object.assign({}, targetValue), sourceValue) + } else { + target[key] = sourceValue + } + }) return target } diff --git a/tests/setData.spec.ts b/tests/setData.spec.ts index e965b1e05..6e0edb35b 100644 --- a/tests/setData.spec.ts +++ b/tests/setData.spec.ts @@ -214,4 +214,36 @@ describe('setData', () => { expect(wrapper.vm.value).toBeInstanceOf(Date) expect(wrapper.vm.value!.toISOString()).toBe('2022-08-11T12:15:54.000Z') }) + + it('should retain prototype methods for constructed objects when calling setData', async () => { + const expectedResult = 'success!' + class TestClass { + constructor(readonly name: string) {} + getResult(): string { + return expectedResult + } + } + + const wrapper = mount( + defineComponent({ + template: '
', + data() { + return { value: new TestClass('test1') } + }, + methods: { + getResult() { + return `${this.value.name}: ${this.value.getResult()}` + } + } + }) + ) + + expect(wrapper.vm.getResult()).toStrictEqual(`test1: ${expectedResult}`) + + await wrapper.setData({ + value: new TestClass('test2') + }) + + expect(wrapper.vm.getResult()).toStrictEqual(`test2: ${expectedResult}`) + }) })