feat!: Update RegisterInstance Decorator for stable decorator api of typescript

This commit is contained in:
2024-08-24 02:21:50 +02:00
parent 4aa12321e8
commit 81873f3689

View File

@@ -1,3 +1,4 @@
import { IdentifierRequiredError } from 'src/interfaces/Exceptions';
import { TSinjex } from '../classes/TSinjex'; import { TSinjex } from '../classes/TSinjex';
import { Identifier } from '../types/Identifier'; import { Identifier } from '../types/Identifier';
import { InitDelegate } from '../types/InitDelegate'; 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. * 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. * @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. * @param identifier The {@link Identifier|identifier} used to register the class in the DI container or the class name if not provided.
* @see {@link Identifier} for more information on identifiers. * @param init An optional initializer {@link InitDelegate|function} which get the constructor of the class
* @param init An optional initializer function which get the constructor of the class
* as input and returns an instance 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. * @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 * @example
* ```ts * ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor()) * \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor())
@@ -22,43 +22,51 @@ import { InitDelegate } from '../types/InitDelegate';
export function RegisterInstance< export function RegisterInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>, TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>( >(
identifier: Identifier, identifier?: Identifier,
init?: InitDelegate< init?: InitDelegate<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> }, TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType> InstanceType<TargetType>
>, >,
) { ) {
return function (constructor: TargetType, ...args: unknown[]): void { return function (
// Get the instance of the DI container constructor: TargetType,
context: ClassDecoratorContext<TargetType>,
): void {
const _identifier = identifier ?? context.name;
if (_identifier == null) throw new IdentifierRequiredError();
const diContainer = TSinjex.getInstance(); const diContainer = TSinjex.getInstance();
let instance: InstanceType<TargetType>; let instance: InstanceType<TargetType>;
/**
* 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) // 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) { get(_target, prop, _receiver) {
if (instance == null) { getAndRegisterInstance();
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
// Return the requested property of the instance // Return the requested property of the instance
return instance[prop as keyof InstanceType<TargetType>]; return instance[prop as keyof InstanceType<TargetType>];
}, },
set(target, prop, value, receiver) { set(_target, prop, value, _receiver) {
if (instance == null) { getAndRegisterInstance();
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
// Set the requested property of the instance // Set the requested property of the instance
return (instance[prop as keyof InstanceType<TargetType>] = return (instance[prop as keyof InstanceType<TargetType>] =
@@ -67,7 +75,6 @@ export function RegisterInstance<
}, },
); );
// Register the lazy proxy in the DI container diContainer.register(_identifier, lazyProxy);
diContainer.register(identifier, lazyProxy);
}; };
} }