All files / functions inject.ts

96% Statements 24/25
88% Branches 22/25
100% Functions 6/6
100% Lines 22/22

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 1141x 1x                                                                     1x             10x 10x           9x   7x 3x   1x     7x 6x 4x           2x 2x   7x                                     14x 14x   3x 2x       1x                   3x   3x          
import { TSinjex } from '../classes/TSinjex.js';
import {
    DependencyResolutionError,
    InitializationError,
    InjectorError,
    NoInstantiationMethodError,
} from '../interfaces/Exceptions.js';
import { Identifier } from '../types/Identifier.js';
import { InitDelegate } from '../types/InitDelegate.js';
 
/**
 * A function to inject a dependency from a DI (Dependency Injection) container into a variable.
 * @template T The type of the dependency 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.
 * @see {@link Identifier} for more information on identifiers.
 * @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.
 * @param isNecessary If true, throws an error if the dependency is not found.
 * @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.
 * @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.
 * @throws An {@link InitializationError} if an error occurs during the initialization process.
 * @example
 * ```ts
 * let myDependency = inject<MyDependency>('MyDependencyIdentifier');
 * ```
 * @example
 * ```ts
 * let logger = inject<ILogger>('ILogger_', (x: ILogger_) => x.getLogger('Tags'), false);
 * ```
 */
export function inject<T, U>(
    identifier: Identifier,
    init?: InitDelegate<T, U> | true,
    isNecessary = true,
): T | U | undefined {
    let instance: T | U | undefined;
 
    const dependency: T | undefined = tryAndCatch(
        () => TSinjex.getInstance().resolve<T>(identifier, isNecessary),
        isNecessary,
        identifier,
        DependencyResolutionError,
    );
 
    if (dependency != null) {
        const initFunction: (() => U) | undefined =
            typeof init === 'function' && dependency != null
                ? (): U => init(dependency)
                : init === true && hasConstructor(dependency)
                  ? (): U => new dependency() as U
                  : undefined;
 
        if (init == null) instance = dependency;
        else if (initFunction != null)
            instance = tryAndCatch(
                initFunction,
                isNecessary,
                identifier,
                InitializationError,
            );
        else if (isNecessary) throw new NoInstantiationMethodError(identifier);
    } else Iif (isNecessary) throw new DependencyResolutionError(identifier);
 
    return instance as 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.
 * @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'
    );
}
 
Zur TypeDoc-Dokumentation