feat!: Update RegisterInstance
Decorator for stable decorator api of typescript
This commit is contained in:
@@ -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);
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user