UNPKG

@stnekroman/tstools

Version:

Set of handy tools for TypeScript development

70 lines (64 loc) 2.7 kB
import { type Types } from '../Types'; interface DeferInstanceDecoratorsMetadata<C extends Object> { decorators: [propertyName: keyof C | string, decorator: PropertyDecorator | MethodDecorator][]; } const DeferInstanceDecoratorsMetadataSymbol = Symbol('DeferInstanceDecoratorsMetadata'); function getDeferInstanceDecoratorsMetadata<C extends Object>( // eslint-disable-next-line @typescript-eslint/no-explicit-any target: any ): DeferInstanceDecoratorsMetadata<C> | undefined { if (target.hasOwnProperty(DeferInstanceDecoratorsMetadataSymbol)) { return target[DeferInstanceDecoratorsMetadataSymbol]; } return undefined; } // eslint-disable-next-line @typescript-eslint/no-explicit-any function getOrCreateDeferInstanceDecoratorsMetadata<C extends Object>(target: any): DeferInstanceDecoratorsMetadata<C> { let metadata: DeferInstanceDecoratorsMetadata<C>; if (target.hasOwnProperty(DeferInstanceDecoratorsMetadataSymbol)) { metadata = target[DeferInstanceDecoratorsMetadataSymbol]; } else { metadata = { decorators: [], }; Object.defineProperty(target, DeferInstanceDecoratorsMetadataSymbol, { value: metadata, }); } return metadata as DeferInstanceDecoratorsMetadata<C>; } export function applyInstanceDecorators<C extends Object, CTR extends Types.Newable<C> | { prototype: unknown }>( instance: C, ctr: CTR, metadata: DeferInstanceDecoratorsMetadata<C> | undefined = getDeferInstanceDecoratorsMetadata<C>(ctr.prototype) ): void { if (metadata) { for (const [propertyName, decorator] of metadata.decorators) { decorator(instance, propertyName as string, Object.getOwnPropertyDescriptor(instance, propertyName)!); } } } export function RunInstanceDecorators<C extends Object>() { return <CTR extends Types.Newable<C> | { prototype: unknown }>(ctr: CTR): CTR => { const metadata: DeferInstanceDecoratorsMetadata<C> | undefined = getDeferInstanceDecoratorsMetadata<C>( ctr.prototype ); if (!metadata) { return ctr; } const newCtr = class extends (ctr as Types.Newable) { constructor(...args: unknown[]) { super(...args); applyInstanceDecorators(this as unknown as C, ctr, metadata); } } as CTR; Object.defineProperty(newCtr, 'name', { value: (ctr as Types.Newable<C>).name }); return newCtr; }; } export function DeferInstanceDecorator<C extends Object>(decorator: PropertyDecorator | MethodDecorator) { return (target: C, propertyName: keyof C | string) => { const metadata: DeferInstanceDecoratorsMetadata<C> = getOrCreateDeferInstanceDecoratorsMetadata<C>(target); metadata.decorators.push([propertyName, decorator]); }; }