diff --git a/src/extend.ts b/src/extend.ts new file mode 100644 index 0000000..d726908 --- /dev/null +++ b/src/extend.ts @@ -0,0 +1,23 @@ +import 'reflect-metadata'; +import { extendRecursively } from './utils'; + +export function Extend(): ClassDecorator { + return (constructor: Function): void => { + const instance: Object = constructor.prototype; + const instanceProto: Object = Object.getPrototypeOf(instance); + + for (const propertyKey of Object.getOwnPropertyNames(instance)) { + const metadataKeys = + propertyKey === 'constructor' + ? Reflect.getOwnMetadataKeys(instance.constructor) + : Reflect.getOwnMetadataKeys(instance, propertyKey); + + extendRecursively( + instance, + metadataKeys, + propertyKey, + instanceProto, + ); + } + }; +} diff --git a/src/utils.ts b/src/utils.ts new file mode 100644 index 0000000..5abca28 --- /dev/null +++ b/src/utils.ts @@ -0,0 +1,81 @@ +export function extendRecursively( + instance: Object, + metadataKeys: any[], // eslint-disable-line + propertyKey: string | symbol, + instanceProto: Object, +): void { + if (propertyKey === 'constructor') + extendConstructorRec(instance, metadataKeys, instanceProto); + else extendPropertyRec(instance, metadataKeys, propertyKey, instanceProto); +} + +function extendConstructorRec( + instance: Object, + metadataKeys: any[], // eslint-disable-line + instanceProto: Object, +) { + if (!instanceProto || !instanceProto.constructor) return; + + const protoMetadataKeys = Reflect.getOwnMetadataKeys( + instanceProto.constructor, + ); + for (const metadataKey of protoMetadataKeys) { + if (metadataKeys.includes(metadataKey)) continue; + + const metadataValue = Reflect.getOwnMetadata( + metadataKey, + instanceProto.constructor, + ); + + Reflect.defineMetadata( + metadataKey, + metadataValue, + instance.constructor, + ); + metadataKeys.push(metadataKey); + } + + extendConstructorRec( + instance, + metadataKeys, + Object.getPrototypeOf(instanceProto), + ); +} + +function extendPropertyRec( + instance: Object, + metadataKeys: any[], // eslint-disable-line + propertyKey: string | symbol, + instanceProto: Object, +) { + if (!instanceProto || !instanceProto.hasOwnProperty(propertyKey)) return; + + const protoMetadataKeys = Reflect.getOwnMetadataKeys( + instanceProto, + propertyKey, + ); + for (const metadataKey of protoMetadataKeys) { + if (metadataKeys.includes(metadataKey)) continue; + + const metadataValue = Reflect.getOwnMetadata( + metadataKey, + instanceProto, + propertyKey, + ); + + Reflect.defineMetadata( + metadataKey, + metadataValue, + instance, + propertyKey, + ); + metadataKeys.push(metadataKey); + } + + extendPropertyRec( + instance, + metadataKeys, + propertyKey, + Object.getPrototypeOf(instanceProto), + ); +}