All files / src/libs/DependencyInjection/decorators Inject.ts

93.75% Statements 15/16
80% Branches 4/5
100% Functions 4/4
93.75% Lines 15/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 777x                                                           7x         23x   23x   23x       23x 190x       23x     411x 190x 54x 54x   14x         136x       411x                      
import { DIContainer } from '../DIContainer';
import { InitDelegate } from '../types/InitDelegate';
 
/**
 * A decorator to inject a dependency from a DI (Dependency Injection) container.
 * The dependency is lazily evaluated when the property is accessed for the first time.
 * This can help avoid issues like circular dependencies and not-found dependencies.
 * @template ClassType The type of the property to be injected.
 * @param identifier The identifier used to resolve the dependency from the DI container.
 * @param init An optional initializer function to transform the dependency before injection.
 * @param necessary Indicates if the dependency is necessary.
 * - If `true`, an error will be thrown if the dependency cannot be resolved.
 * - If `false`, `undefined` will be returned if the dependency cannot be resolved.
 * @returns A decorator function to be applied on the class property.
 * @see {@link DIContainer}
 * @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: string,
    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 = DIContainer.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) {
                            Iif (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