feat!: Update Inject
Decorator for stable decorator api of typescript
Update the function and refactor the typing to reflect the property type
This commit is contained in:
@@ -1,5 +1,6 @@
|
|||||||
import {
|
import {
|
||||||
DependencyResolutionError,
|
DependencyResolutionError,
|
||||||
|
IdentifierRequiredError,
|
||||||
InitializationError,
|
InitializationError,
|
||||||
InjectorError,
|
InjectorError,
|
||||||
NoInstantiationMethodError,
|
NoInstantiationMethodError,
|
||||||
@@ -10,17 +11,17 @@ import { InitDelegate } from '../types/InitDelegate';
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* A decorator to inject a dependency from a DI (Dependency Injection) container into a class property.
|
* A decorator to inject a dependency from a DI (Dependency Injection) container into a class property.
|
||||||
* @template T The type of the dependency to be injected.
|
* @template TargetType The type of the class to inject the dependency into.
|
||||||
* @template U The type of the property to be injected.
|
* @template DependencyType The type of the dependency to be injected.
|
||||||
* @param identifier The identifier used to resolve the class in the DI container.
|
* @template PropertyType The type of the property to be injected.
|
||||||
* @see {@link Identifier} for more information on identifiers.
|
* @param identifier The {@link Identifier|identifier} used to resolve the dependencie in the DI container or the property name if not provided.
|
||||||
* @param init Optional an initializer function to transform the dependency before injection
|
* @param init An optional initializer {@link InitDelegate|function} to transform the dependency before injection
|
||||||
* or true to instantiate the dependency if it has a constructor.
|
* or true to instantiate the dependency if it has a constructor.
|
||||||
* @see {@link InitDelegate} for more information on initializer functions.
|
|
||||||
* @param necessary If true, throws an error if the dependency is not found.
|
* @param necessary If true, throws an error if the dependency is not found.
|
||||||
* @returns The resolved dependency or undefined if the dependency is not necessary
|
* @returns The resolved dependency or undefined if the dependency is not necessary
|
||||||
* and not found, or throws an error if the dependency is necessary and not found.
|
* and not found, or throws an error if the dependency is necessary and not found.
|
||||||
* @throws **Only throws errors if the dependency is necessary.**
|
* @throws **Only throws errors if the dependency is necessary.**
|
||||||
|
* @throws An {@link IdentifierRequiredError} if the identifier is not provided and the class name is not available.
|
||||||
* @throws A {@link DependencyResolutionError} if the dependency is not found.
|
* @throws A {@link DependencyResolutionError} if the dependency is not found.
|
||||||
* @throws A {@link InjectorError} if an error occurs during the injection process.
|
* @throws A {@link InjectorError} if an error occurs during the injection process.
|
||||||
* @throws A {@link NoInstantiationMethodError} if the dependency does not have a constructor.
|
* @throws A {@link NoInstantiationMethodError} if the dependency does not have a constructor.
|
||||||
@@ -40,70 +41,86 @@ import { InitDelegate } from '../types/InitDelegate';
|
|||||||
* }
|
* }
|
||||||
* ```
|
* ```
|
||||||
*/
|
*/
|
||||||
export function Inject<T, U>(
|
export function Inject<TargetType, DependencyType, PropertyType>(
|
||||||
identifier: Identifier,
|
identifier?: Identifier,
|
||||||
init?: InitDelegate<T, U> | true,
|
init?: InitDelegate<DependencyType, PropertyType> | true,
|
||||||
necessary = true,
|
necessary = true,
|
||||||
) {
|
) {
|
||||||
return function (target: unknown, propertyKey: string | symbol): void {
|
return function (
|
||||||
|
constructor: undefined,
|
||||||
|
context: ClassFieldDecoratorContext<TargetType> & {
|
||||||
|
name: PropertyType;
|
||||||
|
},
|
||||||
|
): void {
|
||||||
|
const _identifier = identifier ?? context.name;
|
||||||
|
|
||||||
|
if (_identifier == null && necessary === true)
|
||||||
|
throw new IdentifierRequiredError();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to evaluate the dependency lazily
|
* Function to evaluate the dependency lazily
|
||||||
* to avoid circular dependencies, not found dependencies, etc.
|
* to avoid circular dependencies, not found dependencies, etc.
|
||||||
* @returns The resolved dependency or undefined if the dependency is not found.
|
* @returns The resolved dependency or undefined if the dependency is not found.
|
||||||
*/
|
*/
|
||||||
const resolve = (): T | undefined => {
|
const resolve = (): DependencyType | undefined => {
|
||||||
return TSinjex.getInstance().resolve<T>(identifier, necessary);
|
return TSinjex.getInstance().resolve<DependencyType>(
|
||||||
|
_identifier,
|
||||||
|
necessary,
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
Object.defineProperty(target, propertyKey, {
|
context.addInitializer(function (this: TargetType) {
|
||||||
get() {
|
Object.defineProperty(this, context.name, {
|
||||||
let instance: T | U | undefined;
|
get() {
|
||||||
|
let instance: DependencyType | PropertyType | undefined;
|
||||||
|
|
||||||
const dependency: T | undefined = tryAndCatch(
|
const dependency: DependencyType | undefined = tryAndCatch(
|
||||||
() => resolve(),
|
() => resolve(),
|
||||||
necessary,
|
necessary,
|
||||||
identifier,
|
_identifier,
|
||||||
DependencyResolutionError,
|
DependencyResolutionError,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (dependency != null) {
|
if (dependency != null) {
|
||||||
const initFunction: (() => U) | undefined =
|
const initFunction: (() => PropertyType) | undefined =
|
||||||
typeof init === 'function' && dependency != null
|
typeof init === 'function' && dependency != null
|
||||||
? (): U => init(dependency)
|
? (): PropertyType => init(dependency)
|
||||||
: init === true && hasConstructor(dependency)
|
: init === true && hasConstructor(dependency)
|
||||||
? (): U => new dependency() as U
|
? (): PropertyType =>
|
||||||
: undefined;
|
new dependency() as PropertyType
|
||||||
|
: undefined;
|
||||||
|
|
||||||
if (init == null) instance = dependency;
|
if (init == null) instance = dependency;
|
||||||
else if (initFunction != null)
|
else if (initFunction != null)
|
||||||
instance = tryAndCatch(
|
instance = tryAndCatch(
|
||||||
initFunction,
|
initFunction,
|
||||||
necessary,
|
necessary,
|
||||||
identifier,
|
_identifier,
|
||||||
InitializationError,
|
InitializationError,
|
||||||
);
|
);
|
||||||
else if (necessary)
|
else if (necessary)
|
||||||
throw new NoInstantiationMethodError(identifier);
|
throw new NoInstantiationMethodError(_identifier);
|
||||||
} else if (necessary)
|
} else if (necessary)
|
||||||
throw new DependencyResolutionError(identifier);
|
throw new DependencyResolutionError(_identifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace itself with the resolved dependency
|
||||||
|
* for performance reasons.
|
||||||
|
*/
|
||||||
|
Object.defineProperty(this, context.name, {
|
||||||
|
value: instance,
|
||||||
|
writable: false,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
return instance;
|
||||||
|
},
|
||||||
/**
|
/**
|
||||||
* Replace itself with the resolved dependency
|
* Make the property configurable to allow replacing it
|
||||||
* for performance reasons.
|
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, propertyKey, {
|
configurable: true,
|
||||||
value: instance,
|
});
|
||||||
writable: false,
|
|
||||||
enumerable: false,
|
|
||||||
configurable: false,
|
|
||||||
});
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
},
|
|
||||||
/**
|
|
||||||
* Make the property configurable to allow replacing it
|
|
||||||
*/
|
|
||||||
configurable: true,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user