From 81873f368946ad64882b0c2a190190ada2d1f92a Mon Sep 17 00:00:00 2001 From: Max P Date: Sat, 24 Aug 2024 02:21:50 +0200 Subject: [PATCH] feat!: Update `RegisterInstance` Decorator for stable decorator api of typescript --- src/decorators/RegisterInstance.ts | 63 +++++++++++++++++------------- 1 file changed, 35 insertions(+), 28 deletions(-) diff --git a/src/decorators/RegisterInstance.ts b/src/decorators/RegisterInstance.ts index d638f6e..959baa4 100644 --- a/src/decorators/RegisterInstance.ts +++ b/src/decorators/RegisterInstance.ts @@ -1,3 +1,4 @@ +import { IdentifierRequiredError } from 'src/interfaces/Exceptions'; import { TSinjex } from '../classes/TSinjex'; import { Identifier } from '../types/Identifier'; import { InitDelegate } from '../types/InitDelegate'; @@ -5,12 +6,11 @@ import { InitDelegate } from '../types/InitDelegate'; /** * A decorator to register an instance of a class in the DI (Dependency Injection) container. * @template TargetType The type of the class whose instance is to be registered. - * @param identifier The identifier used to register the instance in the DI container. - * @see {@link Identifier} for more information on identifiers. - * @param init An optional initializer function which get the constructor of the class + * @param identifier The {@link Identifier|identifier} used to register the class in the DI container or the class name if not provided. + * @param init An optional initializer {@link InitDelegate|function} which get the constructor of the class * as input and returns an instance of the class. - * @see {@link InitDelegate} for more information on initializer functions. * @returns The decorator function to be applied on the class. + * @throws An {@link IdentifierRequiredError} if the identifier is not provided and the class name is not available. * @example * ```ts * \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor()) @@ -22,43 +22,51 @@ import { InitDelegate } from '../types/InitDelegate'; export function RegisterInstance< TargetType extends new (..._args: unknown[]) => InstanceType, >( - identifier: Identifier, + identifier?: Identifier, init?: InitDelegate< TargetType & { new (..._args: unknown[]): InstanceType }, InstanceType >, ) { - return function (constructor: TargetType, ...args: unknown[]): void { - // Get the instance of the DI container + return function ( + constructor: TargetType, + context: ClassDecoratorContext, + ): void { + const _identifier = identifier ?? context.name; + + if (_identifier == null) throw new IdentifierRequiredError(); + const diContainer = TSinjex.getInstance(); let instance: InstanceType; + /** + * Get the instance of the class + * and replace the lazy proxy with the instance + * for performance optimization. + */ + const getAndRegisterInstance = (): void => { + if (instance == null) { + if (init) { + instance = init(constructor); + } else { + instance = new constructor(); + } + } + diContainer.register(_identifier, instance); + }; + // Create a proxy to instantiate the class when needed (Lazy Initialization) - let lazyProxy: unknown = new Proxy( + const lazyProxy: unknown = new Proxy( {}, { - get(target, prop, receiver) { - if (instance == null) { - if (init) { - instance = init(constructor); - } else { - instance = new constructor(...args); - } - } - lazyProxy = instance; + get(_target, prop, _receiver) { + getAndRegisterInstance(); // Return the requested property of the instance return instance[prop as keyof InstanceType]; }, - set(target, prop, value, receiver) { - if (instance == null) { - if (init) { - instance = init(constructor); - } else { - instance = new constructor(...args); - } - } - lazyProxy = instance; + set(_target, prop, value, _receiver) { + getAndRegisterInstance(); // Set the requested property of the instance return (instance[prop as keyof InstanceType] = @@ -67,7 +75,6 @@ export function RegisterInstance< }, ); - // Register the lazy proxy in the DI container - diContainer.register(identifier, lazyProxy); + diContainer.register(_identifier, lazyProxy); }; }