Add error handling and constructor checks to Inject
- Import additional exception classes from `src/interfaces/Exceptions` - Modify `Inject` function to: - Accept `init` parameter as a function or `true` for instantiation - Throw specific errors: `DependencyResolutionError`, `InjectorError`, `NoInstantiationMethodError` - Ensure necessary dependencies are handled properly - Define property with `Object.defineProperty` for performance - Add `hasConstructor` helper function to check if an object has a constructor
This commit is contained in:
@@ -1,3 +1,9 @@
|
|||||||
|
import {
|
||||||
|
DependencyResolutionError,
|
||||||
|
InitializationError,
|
||||||
|
InjectorError,
|
||||||
|
NoInstantiationMethodError,
|
||||||
|
} 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';
|
||||||
@@ -8,12 +14,16 @@ import { InitDelegate } from '../types/InitDelegate';
|
|||||||
* @template U The type of the property to be injected.
|
* @template U The type of the property to be injected.
|
||||||
* @param identifier The identifier used to resolve the class in the DI container.
|
* @param identifier The identifier used to resolve the class in the DI container.
|
||||||
* @see {@link Identifier} for more information on identifiers.
|
* @see {@link Identifier} for more information on identifiers.
|
||||||
* @param init An optional initializer function to transform the dependency before injection.
|
* @param init Optional an initializer function to transform the dependency before injection
|
||||||
|
* or true to instantiate the dependency if it has a constructor.
|
||||||
* @see {@link InitDelegate} for more information on initializer functions.
|
* @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 A {@link DependencyResolutionError} if the dependency is not found and necessary.
|
* @throws **Only throws errors if the dependency is necessary.**
|
||||||
|
* @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 NoInstantiationMethodError} if the dependency does not have a constructor.
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* class MyClass {
|
* class MyClass {
|
||||||
@@ -31,7 +41,7 @@ import { InitDelegate } from '../types/InitDelegate';
|
|||||||
*/
|
*/
|
||||||
export function Inject<T, U>(
|
export function Inject<T, U>(
|
||||||
identifier: Identifier,
|
identifier: Identifier,
|
||||||
init?: InitDelegate<T, U>,
|
init?: InitDelegate<T, U> | true,
|
||||||
necessary = true,
|
necessary = true,
|
||||||
) {
|
) {
|
||||||
return function (target: unknown, propertyKey: string | symbol): void {
|
return function (target: unknown, propertyKey: string | symbol): void {
|
||||||
@@ -51,12 +61,48 @@ export function Inject<T, U>(
|
|||||||
get() {
|
get() {
|
||||||
// If the property is not defined, evaluate the dependency
|
// If the property is not defined, evaluate the dependency
|
||||||
if (!this.hasOwnProperty(privatePropertyKey)) {
|
if (!this.hasOwnProperty(privatePropertyKey)) {
|
||||||
if (init) {
|
if (init != null) {
|
||||||
try {
|
try {
|
||||||
this[privatePropertyKey] = init(evaluate() as T);
|
const dependency = evaluate();
|
||||||
|
|
||||||
|
if (dependency != null) {
|
||||||
|
if (typeof init === 'function') {
|
||||||
|
try {
|
||||||
|
this[privatePropertyKey] =
|
||||||
|
init(dependency);
|
||||||
|
} catch (error) {
|
||||||
|
if (necessary)
|
||||||
|
throw new InitializationError(
|
||||||
|
identifier,
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else if (
|
||||||
|
init === true &&
|
||||||
|
hasConstructor(dependency)
|
||||||
|
) {
|
||||||
|
this[privatePropertyKey] = new dependency();
|
||||||
|
} else
|
||||||
|
throw new NoInstantiationMethodError(
|
||||||
|
identifier,
|
||||||
|
);
|
||||||
|
} else if (necessary) {
|
||||||
|
throw new DependencyResolutionError(identifier);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
if (necessary) {
|
if (necessary) {
|
||||||
throw error;
|
if (
|
||||||
|
!(
|
||||||
|
error instanceof
|
||||||
|
NoInstantiationMethodError
|
||||||
|
) &&
|
||||||
|
!(
|
||||||
|
error instanceof
|
||||||
|
DependencyResolutionError
|
||||||
|
)
|
||||||
|
)
|
||||||
|
throw new InjectorError(identifier, error);
|
||||||
|
else throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -64,14 +110,37 @@ export function Inject<T, U>(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Replace itself with the resolved dependency
|
||||||
|
* for performance reasons.
|
||||||
|
*/
|
||||||
|
Object.defineProperty(this, propertyKey, {
|
||||||
|
value: this[privatePropertyKey],
|
||||||
|
writable: false,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: false,
|
||||||
|
});
|
||||||
|
|
||||||
return this[privatePropertyKey];
|
return this[privatePropertyKey];
|
||||||
},
|
},
|
||||||
// Not necessary to set the property
|
/**
|
||||||
// set(value: PropertieType) {
|
* Make the property configurable to allow replacing it
|
||||||
// this[privatePropertyKey] = value;
|
*/
|
||||||
// },
|
configurable: true,
|
||||||
enumerable: true,
|
|
||||||
configurable: false,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if an object has a constructor.
|
||||||
|
* @param obj The object to check.
|
||||||
|
* @returns True if the object has a constructor, false otherwise.
|
||||||
|
*/
|
||||||
|
function hasConstructor<T>(obj: T): obj is T & { new (): unknown } {
|
||||||
|
const _obj = obj as unknown as { prototype?: { constructor?: unknown } };
|
||||||
|
|
||||||
|
return (
|
||||||
|
_obj?.prototype != null &&
|
||||||
|
typeof _obj.prototype.constructor === 'function'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
Reference in New Issue
Block a user