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 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | 1x 1x 1x 10x 10x 10x 10x 10x 10x 9x 7x 2x 1x 7x 5x 3x 2x 1x 2x 1x 6x 6x 13x 13x 2x 2x 3x 3x | import { DependencyResolutionError, InitializationError, InjectorError, NoInstantiationMethodError, } from 'src/interfaces/Exceptions'; import { TSinjex } from '../classes/TSinjex'; import { Identifier } from '../types/Identifier'; 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 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 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 **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 * 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> | true, necessary = true, ) { return function (target: unknown, propertyKey: string | symbol): void { /** * Function to evaluate the dependency lazily * to avoid circular dependencies, not found dependencies, etc. * @returns The resolved dependency or undefined if the dependency is not found. */ const resolve = (): T | undefined => { return TSinjex.getInstance().resolve<T>(identifier, necessary); }; Object.defineProperty(target, propertyKey, { get() { let instance: T | U | undefined; const dependency: T | undefined = tryAndCatch( () => resolve(), necessary, 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, necessary, identifier, InitializationError, ); else if (necessary) throw new NoInstantiationMethodError(identifier); } else if (necessary) throw new DependencyResolutionError(identifier); /** * Replace itself with the resolved dependency * for performance reasons. */ Object.defineProperty(this, propertyKey, { value: instance, writable: false, enumerable: false, configurable: false, }); return instance; }, /** * Make the property configurable to allow replacing it */ configurable: true, }); }; } /** * 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 Ereturn 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' ); } |