feat: Add initialization error handling and refactor Inject
This commit is contained in:
@@ -24,6 +24,7 @@ import { InitDelegate } from '../types/InitDelegate';
|
|||||||
* @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.
|
||||||
|
* @throws An {@link InitializationError} if an error occurs during the initialization process.
|
||||||
* @example
|
* @example
|
||||||
* ```ts
|
* ```ts
|
||||||
* class MyClass {
|
* class MyClass {
|
||||||
@@ -45,83 +46,59 @@ export function Inject<T, U>(
|
|||||||
necessary = true,
|
necessary = true,
|
||||||
) {
|
) {
|
||||||
return function (target: unknown, propertyKey: string | symbol): void {
|
return function (target: unknown, propertyKey: string | symbol): void {
|
||||||
// Unique symbol to store the private property
|
/**
|
||||||
const privatePropertyKey: unique symbol = Symbol();
|
* Function to evaluate the dependency lazily
|
||||||
// Get the DI container instance
|
* to avoid circular dependencies, not found dependencies, etc.
|
||||||
const diContainer = TSinjex.getInstance();
|
* @returns The resolved dependency or undefined if the dependency is not found.
|
||||||
|
*/
|
||||||
// Function to evaluate the dependency lazily
|
const resolve = (): T | undefined => {
|
||||||
// to avoid circular dependencies, not found dependencies, etc.
|
return TSinjex.getInstance().resolve<T>(identifier, necessary);
|
||||||
const evaluate = (): T | undefined => {
|
|
||||||
return diContainer.resolve<T>(identifier, necessary);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Define the property
|
|
||||||
Object.defineProperty(target, propertyKey, {
|
Object.defineProperty(target, propertyKey, {
|
||||||
get() {
|
get() {
|
||||||
// If the property is not defined, evaluate the dependency
|
let instance: T | U | undefined;
|
||||||
if (!this.hasOwnProperty(privatePropertyKey)) {
|
|
||||||
if (init != null) {
|
|
||||||
try {
|
|
||||||
const dependency = evaluate();
|
|
||||||
|
|
||||||
if (dependency != null) {
|
const dependency: T | undefined = tryAndCatch(
|
||||||
if (typeof init === 'function') {
|
() => resolve(),
|
||||||
try {
|
necessary,
|
||||||
this[privatePropertyKey] =
|
identifier,
|
||||||
init(dependency);
|
DependencyResolutionError,
|
||||||
} catch (error) {
|
);
|
||||||
if (necessary)
|
|
||||||
throw new InitializationError(
|
if (dependency != null) {
|
||||||
identifier,
|
const initFunction: (() => U) | undefined =
|
||||||
error,
|
typeof init === 'function' && dependency != null
|
||||||
);
|
? (): U => init(dependency)
|
||||||
}
|
: init === true && hasConstructor(dependency)
|
||||||
} else if (
|
? (): U => new dependency() as U
|
||||||
init === true &&
|
: undefined;
|
||||||
hasConstructor(dependency)
|
|
||||||
) {
|
if (init == null) instance = dependency;
|
||||||
this[privatePropertyKey] = new dependency();
|
else if (initFunction != null)
|
||||||
} else
|
instance = tryAndCatch(
|
||||||
throw new NoInstantiationMethodError(
|
initFunction,
|
||||||
identifier,
|
necessary,
|
||||||
);
|
identifier,
|
||||||
} else if (necessary) {
|
InitializationError,
|
||||||
throw new DependencyResolutionError(identifier);
|
);
|
||||||
}
|
else if (necessary)
|
||||||
} catch (error) {
|
throw new NoInstantiationMethodError(identifier);
|
||||||
if (necessary) {
|
} else if (necessary)
|
||||||
if (
|
throw new DependencyResolutionError(identifier);
|
||||||
!(
|
|
||||||
error instanceof
|
|
||||||
NoInstantiationMethodError
|
|
||||||
) &&
|
|
||||||
!(
|
|
||||||
error instanceof
|
|
||||||
DependencyResolutionError
|
|
||||||
)
|
|
||||||
)
|
|
||||||
throw new InjectorError(identifier, error);
|
|
||||||
else throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
this[privatePropertyKey] = evaluate();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Replace itself with the resolved dependency
|
* Replace itself with the resolved dependency
|
||||||
* for performance reasons.
|
* for performance reasons.
|
||||||
*/
|
*/
|
||||||
Object.defineProperty(this, propertyKey, {
|
Object.defineProperty(this, propertyKey, {
|
||||||
value: this[privatePropertyKey],
|
value: instance,
|
||||||
writable: false,
|
writable: false,
|
||||||
enumerable: false,
|
enumerable: false,
|
||||||
configurable: false,
|
configurable: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
return this[privatePropertyKey];
|
return instance;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Make the property configurable to allow replacing it
|
* Make the property configurable to allow replacing it
|
||||||
@@ -131,6 +108,34 @@ export function Inject<T, U>(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Tries to execute a function and catches any errors that occur.
|
||||||
|
* If the function is necessary and an error occurs, it throws the error
|
||||||
|
* with the specified error class and identifier.
|
||||||
|
* @param fn The function to execute.
|
||||||
|
* @param necessary If true, throws an error if an error occurs.
|
||||||
|
* @param identifier The identifier of the dependency.
|
||||||
|
* @param errorClass The error class to throw if an error occurs.
|
||||||
|
* @returns The result of the function or undefined if an error occurs and the function is not necessary.
|
||||||
|
*/
|
||||||
|
function tryAndCatch<ReturnType, ErrorType>(
|
||||||
|
fn: () => ReturnType,
|
||||||
|
necessary: boolean,
|
||||||
|
identifier?: Identifier,
|
||||||
|
errorClass?: ErrorType,
|
||||||
|
): ReturnType | undefined {
|
||||||
|
try {
|
||||||
|
return fn();
|
||||||
|
} catch (error) {
|
||||||
|
if (necessary)
|
||||||
|
throw new (errorClass != null ? errorClass : error)(
|
||||||
|
identifier ?? 'not specified',
|
||||||
|
error,
|
||||||
|
);
|
||||||
|
else return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if an object has a constructor.
|
* Checks if an object has a constructor.
|
||||||
* @param obj The object to check.
|
* @param obj The object to check.
|
||||||
|
Reference in New Issue
Block a user