All files / decorators Inject.ts

100% Statements 16/16
100% Branches 5/5
100% Functions 4/4
100% Lines 16/16

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  1x                                                           1x         4x   4x   4x       4x 4x       4x     4x 4x 2x 2x   1x 1x       2x       2x                      
import { Identifier } from 'src/types/Identifier';
import { TSinjex } from '../classes/TSinjex';
import { InitDelegate } from '../types/InitDelegate';
 
/**
 * 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 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 An optional initializer function to transform the dependency before injection.
 * @see {@link InitDelegate} for more information on initializer functions.
 * @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
 * 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.
 * @example
 * ```ts
 * class MyClass {
 *   \@Inject<MyDependency>('MyDependencyIdentifier')
 *   private myDependency!: MyDependency;
 * }
 * ```
 * @example
 * ```ts
 * class MyClass {
 *   \@Inject('ILogger_', (x: ILogger_) => x.getLogger('Tags'), false)
 *   private _logger?: ILogger;
 * }
 * ```
 */
export function Inject<T, U>(
    identifier: Identifier,
    init?: InitDelegate<T, U>,
    necessary = true,
) {
    return function (target: unknown, propertyKey: string | symbol): void {
        // Unique symbol to store the private property
        const privatePropertyKey: unique symbol = Symbol();
        // Get the DI container instance
        const diContainer = TSinjex.getInstance();
 
        // Function to evaluate the dependency lazily
        // to avoid circular dependencies, not found dependencies, etc.
        const evaluate = (): T | undefined => {
            return diContainer.resolve<T>(identifier, necessary);
        };
 
        // Define the property
        Object.defineProperty(target, propertyKey, {
            get() {
                // If the property is not defined, evaluate the dependency
                if (!this.hasOwnProperty(privatePropertyKey)) {
                    if (init) {
                        try {
                            this[privatePropertyKey] = init(evaluate() as T);
                        } catch (error) {
                            if (necessary) {
                                throw error;
                            }
                        }
                    } else {
                        this[privatePropertyKey] = evaluate();
                    }
                }
 
                return this[privatePropertyKey];
            },
            // Not necessary to set the property
            // set(value: PropertieType) {
            //     this[privatePropertyKey] = value;
            // },
            enumerable: true,
            configurable: false,
        });
    };
}
 
Zur TypeDoc-Dokumentation