import { given } from "@nivinjoseph/n-defensive"; import { ClassDefinition } from "@nivinjoseph/n-util"; // Polyfill Symbol.metadata for runtimes that don't yet ship the TC39 decorator // metadata well-known symbol. Uses Symbol.for so independent copies of this // module (or other libraries polyfilling the same way) converge on a single // symbol identity instead of creating private, non-interoperable symbols. // @ts-expect-error Symbol.metadata may not be declared by the current lib target Symbol.metadata ??= Symbol.for("Symbol.metadata"); export const injectionsKey = Symbol.for("@nivinjoseph/n-ject/inject"); /** * Decorator that marks a class for dependency injection. * Specifies the dependencies that should be injected into the class constructor. * * @param dependencies - The keys of the dependencies to inject * @returns A class decorator that marks the class for dependency injection * * @example * ```typescript * @inject("userService", "logger") * class UserController * { * private readonly _userService: UserService; * private readonly _logger: Logger; * * constructor(userService: UserService, logger: Logger) * { * given(userService, "userService").ensureHasValue().ensureIsObject(); * given(logger, "logger").ensureHasValue().ensureIsObject(); * * this._userService = userService; * this._logger = logger; * } * } * ``` */ export function inject>(...dependencies: [string, ...Array]): InjectClassDecorator { const decorator: InjectClassDecorator = (_, context) => { given(context, "context") // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition .ensure(t => t.kind === "class", "inject should only be used on classes"); context.metadata[injectionsKey] = dependencies; }; return decorator; } export type InjectClassDecorator> = ( value: This, context: ClassDecoratorContext ) => void;