From a073b5ea218dd4da9325fe980f15c1538980500e Mon Sep 17 00:00:00 2001 From: Cody Tseng <64680921+CodyTseng@users.noreply.github.com> Date: Thu, 23 May 2024 02:29:21 +0800 Subject: [PATCH] fix: lookup up property descriptors on the prototype (#1258) --- src/TransformOperationExecutor.ts | 10 ++++- test/functional/basic-functionality.spec.ts | 49 +++++++++++++++++++++ 2 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/TransformOperationExecutor.ts b/src/TransformOperationExecutor.ts index 0533f03df..fe1da8e32 100644 --- a/src/TransformOperationExecutor.ts +++ b/src/TransformOperationExecutor.ts @@ -295,7 +295,7 @@ export class TransformOperationExecutor { // if newValue is a source object that has method that match newKeyName then skip it if (newValue.constructor.prototype) { - const descriptor = Object.getOwnPropertyDescriptor(newValue.constructor.prototype, newValueKey); + const descriptor = this.getPropertyDescriptor(newValue.constructor.prototype, newValueKey); if ( (this.transformationType === TransformationType.PLAIN_TO_CLASS || this.transformationType === TransformationType.CLASS_TO_CLASS) && @@ -544,4 +544,12 @@ export class TransformOperationExecutor { return this.options.groups.some(optionGroup => groups.includes(optionGroup)); } + + private getPropertyDescriptor(obj: any, key: PropertyKey): PropertyDescriptor | undefined { + const descriptor = Object.getOwnPropertyDescriptor(obj, key); + if (descriptor) return descriptor; + + const prototype = Object.getPrototypeOf(obj); + return prototype ? this.getPropertyDescriptor(prototype, key) : undefined; + } } diff --git a/test/functional/basic-functionality.spec.ts b/test/functional/basic-functionality.spec.ts index 9705b405d..f3d002fdc 100644 --- a/test/functional/basic-functionality.spec.ts +++ b/test/functional/basic-functionality.spec.ts @@ -1664,6 +1664,55 @@ describe('basic functionality', () => { expect(transformedUser).toEqual(likeUser); }); + it('should expose inherited method and accessors that have @Expose()', () => { + class User { + firstName: string; + lastName: string; + + @Expose() + get name() { + return this.firstName + ' ' + this.lastName; + } + + @Expose() + getName() { + return this.firstName + ' ' + this.lastName; + } + } + class Programmer extends User { + language: string; + } + + const programmer = new Programmer(); + programmer.firstName = 'Umed'; + programmer.lastName = 'Khudoiberdiev'; + programmer.language = 'en'; + + const fromPlainProgrammer = { + firstName: 'Umed', + lastName: 'Khudoiberdiev', + language: 'en', + }; + + const plainProgrammer: any = instanceToPlain(programmer); + expect(plainProgrammer).not.toBeInstanceOf(Programmer); + expect(plainProgrammer).toEqual({ + firstName: 'Umed', + lastName: 'Khudoiberdiev', + language: 'en', + name: 'Umed Khudoiberdiev', + getName: 'Umed Khudoiberdiev', + }); + + const transformedProgrammer = plainToInstance(Programmer, fromPlainProgrammer); + expect(transformedProgrammer).toBeInstanceOf(Programmer); + const likeProgrammer = new Programmer(); + likeProgrammer.firstName = 'Umed'; + likeProgrammer.lastName = 'Khudoiberdiev'; + likeProgrammer.language = 'en'; + expect(transformedProgrammer).toEqual(likeProgrammer); + }); + it('should transform array', () => { defaultMetadataStorage.clear();