Feature/integrate register instance in register (#2)

* refactor: consolidate registration decorators

- Introduced `Register` decorator to handle class and instance registration in the DI container.
- Deprecated `RegisterInstance` in favor of `Register`, which now internally handles instance registration.
- Added support for marking dependencies as deprecated with a warning logged upon first resolution.
- Updated documentation with examples and notes on deprecation.

* tests: add mode parameter to RegisterInstanceDecorator

- Introduced a `mode` parameter to the `test_RegisterInstanceDecorator` function allowing 'instance' or 'standalone' modes.
- Updated test cases to utilize the new `mode` parameter when registering an instance.
- Disabled specific ESLint rule in `Decorators.test.ts` for deprecation warnings.
- Added an additional test call to `test_RegisterInstanceDecorator` with 'instance' mode.

* docs: Reflect changes to changelog

* refactor: add region tags for overloads in Register.ts

* docs: Reflect changes to changelog

* refactor: consolidate registration decorators

- Introduced `Register` decorator to handle class and instance registration in the DI container.
- Deprecated `RegisterInstance` in favor of `Register`, which now internally handles instance registration.
- Added support for marking dependencies as deprecated with a warning logged upon first resolution.
- Updated documentation with examples and notes on deprecation.

* tests: add mode parameter to RegisterInstanceDecorator

- Introduced a `mode` parameter to the `test_RegisterInstanceDecorator` function allowing 'instance' or 'standalone' modes.
- Updated test cases to utilize the new `mode` parameter when registering an instance.
- Disabled specific ESLint rule in `Decorators.test.ts` for deprecation warnings.
- Added an additional test call to `test_RegisterInstanceDecorator` with 'instance' mode.

* docs: Reflect changes to changelog

* refactor: add region tags for overloads in Register.ts

* docs: Reflect changes to changelog

* docs: Reflect changes to changelog und push version
This commit is contained in:
20Max01
2025-03-14 13:43:10 +01:00
committed by GitHub
parent 9660d77e0c
commit 95ef003e99
6 changed files with 276 additions and 48 deletions

View File

@@ -282,6 +282,7 @@ export function test_RegisterInstanceDecorator(
Container: ITSinjex_,
// eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
registerInstance: Function,
mode: 'instance' | 'standalone' = 'standalone',
): void {
describe('RegisterInstance Decorator Tests', () => {
let container: ITSinjex;
@@ -295,7 +296,10 @@ export function test_RegisterInstanceDecorator(
});
it('should register an instance of a dependency', () => {
@registerInstance('InstanceIdentifier')
@registerInstance(
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass {
private readonly _dependency!: any;
@@ -337,7 +341,10 @@ export function test_RegisterInstanceDecorator(
});
it('should register an instance of a dependency and get it on set', () => {
@registerInstance('InstanceIdentifier')
@registerInstance(
'InstanceIdentifier',
mode === 'instance' ? 'instance' : undefined,
)
class TestClass {
private readonly _dependency!: any;

View File

@@ -1,3 +1,4 @@
/* eslint-disable deprecation/deprecation */
import { TSinjex } from 'src/classes/TSinjex';
import { Inject } from 'src/decorators/Inject';
import { Register } from 'src/decorators/Register';
@@ -13,3 +14,5 @@ test_InjectDecorator(TSinjex, Inject);
test_RegisterDecorator(TSinjex, Register);
test_RegisterInstanceDecorator(TSinjex, RegisterInstance);
test_RegisterInstanceDecorator(TSinjex, Register, 'instance');

View File

@@ -1,6 +1,134 @@
import { InitDelegate } from 'src/types/InitDelegate';
import { TSinjex } from '../classes/TSinjex';
import { Identifier } from '../types/Identifier';
//#region Overloads
/**
* A decorator to register a class in the **TSinjex** DI (Dependency Injection) container.
* @template TargetType The type of the class to be registered.
* @param identifier The identifier used to register the class in the DI container.
* @see {@link Identifier} for more information on identifiers.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@Register('MyClassIdentifier')
* class MyClass {
* // ...
* }
* ```
* @example
* ```ts
* \@Register('MyClassIdentifier', true)
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void;
/**
* 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 shouldRegister Set to 'instance' to register the instance in the DI container
* with an empty constructor.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', 'instance')
* class MyClass {
* // ...
* }
* ```
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', 'instance', true)
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
shouldRegister: 'instance',
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void;
/**
* 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
* as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor())
* class MyClass {
* // ...
* }
* ```
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor(), true)
* class MyClass {
* // ...
* }
* ```
*/
export function Register<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
init?: InitDelegate<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType>
>,
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void;
//#endregion Overloads
// eslint-disable-next-line jsdoc/require-jsdoc
export function Register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
arg1?:
| undefined
| boolean
| InitDelegate<TargetType, InstanceType<TargetType>>
| 'instance',
arg2?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void {
const deprecated = typeof arg1 === 'boolean' ? arg1 : arg2;
const init = typeof arg1 === 'function' ? arg1 : undefined;
const shouldRegisterInstance = arg1 === 'instance';
if (init == undefined && shouldRegisterInstance !== true) {
return _register(identifier, deprecated);
} else {
return _registerInstance(identifier, init, deprecated);
}
}
/**
* A decorator to register a class in the **TSinjex** DI (Dependency Injection) container.
* @template TargetType The type of the class to be registered.
@@ -17,7 +145,7 @@ import { Identifier } from '../types/Identifier';
* }
* ```
*/
export function Register<
function _register<
TargetType extends new (...args: unknown[]) => InstanceType<TargetType>,
>(identifier: Identifier, deprecated?: boolean) {
return function (constructor: TargetType, ...args: unknown[]): void {
@@ -28,3 +156,111 @@ export function Register<
diContainer.register(identifier, constructor, deprecated);
};
}
/**
* 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
* as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class.
* @example
* ```ts
* \@RegisterInstance('MyClassInstanceIdentifier', (constructor) => new constructor())
* class MyClass {
* // ...
* }
* ```
*/
function _registerInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
identifier: Identifier,
init?: InitDelegate<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType>
>,
deprecated?: boolean,
) {
return function (constructor: TargetType, ...args: unknown[]): void {
// Get the instance of the DI container
const diContainer = TSinjex.getInstance();
let instance: InstanceType<TargetType>;
// Create a proxy to instantiate the class when needed (Lazy Initialization)
let lazyProxy: unknown = new Proxy(
{},
{
get(target, prop, receiver) {
({ instance, lazyProxy } = initializeInstance<TargetType>(
instance,
init,
constructor,
args,
lazyProxy,
));
// Return the requested property of the instance
return instance[prop as keyof InstanceType<TargetType>];
},
set(target, prop, value, receiver) {
({ instance, lazyProxy } = initializeInstance<TargetType>(
instance,
init,
constructor,
args,
lazyProxy,
));
// Set the requested property of the instance
return (instance[prop as keyof InstanceType<TargetType>] =
value);
},
},
);
// Register the lazy proxy in the DI container
diContainer.register(identifier, lazyProxy, deprecated);
};
}
/**
* Initializes the instance of the class.
* @template TargetType The type of the class whose instance is to be initialized.
* @param instance The instance of the class to be initialized.
* @param init The optional initializer function to initialize the instance.
* @param constructor The constructor of the class.
* @param args The arguments to be passed to the constructor of the class.
* @param lazyProxy The lazy proxy to instantiate the class when needed.
* @returns The initialized instance and the lazy proxy.
*/
function initializeInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
>(
instance: InstanceType<TargetType>,
init:
| InitDelegate<
TargetType &
(new (..._args: unknown[]) => InstanceType<TargetType>),
InstanceType<TargetType>
>
| undefined,
constructor: TargetType,
args: unknown[],
lazyProxy: unknown,
): { instance: InstanceType<TargetType>; lazyProxy: unknown } {
if (instance == null) {
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
return { instance, lazyProxy };
}

View File

@@ -1,4 +1,4 @@
import { TSinjex } from '../classes/TSinjex';
import { Register } from './Register';
import { Identifier } from '../types/Identifier';
import { InitDelegate } from '../types/InitDelegate';
@@ -9,6 +9,8 @@ import { InitDelegate } from '../types/InitDelegate';
* @see {@link Identifier} for more information on identifiers.
* @param init An optional initializer function which get the constructor of the class
* as input and returns an instance of the class.
* @param deprecated If true, the dependency is deprecated and a warning
* is logged only once upon the first resolution of the dependency.
* @see {@link InitDelegate} for more information on initializer functions.
* @returns The decorator function to be applied on the class.
* @example
@@ -18,6 +20,7 @@ import { InitDelegate } from '../types/InitDelegate';
* // ...
* }
* ```
* @deprecated Use {@link Register} instead. This decorator already uses the {@link Register} decorator internally.
*/
export function RegisterInstance<
TargetType extends new (..._args: unknown[]) => InstanceType<TargetType>,
@@ -27,47 +30,10 @@ export function RegisterInstance<
TargetType & { new (..._args: unknown[]): InstanceType<TargetType> },
InstanceType<TargetType>
>,
) {
return function (constructor: TargetType, ...args: unknown[]): void {
// Get the instance of the DI container
const diContainer = TSinjex.getInstance();
let instance: InstanceType<TargetType>;
deprecated?: boolean,
): (constructor: TargetType, ...args: unknown[]) => void {
const initDelegate = typeof init === 'function' ? init : undefined;
// Create a proxy to instantiate the class when needed (Lazy Initialization)
let lazyProxy: unknown = new Proxy(
{},
{
get(target, prop, receiver) {
if (instance == null) {
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
// Return the requested property of the instance
return instance[prop as keyof InstanceType<TargetType>];
},
set(target, prop, value, receiver) {
if (instance == null) {
if (init) {
instance = init(constructor);
} else {
instance = new constructor(...args);
}
}
lazyProxy = instance;
// Set the requested property of the instance
return (instance[prop as keyof InstanceType<TargetType>] =
value);
},
},
);
// Register the lazy proxy in the DI container
diContainer.register(identifier, lazyProxy);
};
if (initDelegate) return Register(identifier, initDelegate, deprecated);
else return Register(identifier, 'instance', deprecated);
}