@Inject

Declare Dependencies Explicitly

Every constructor parameter that the container should resolve needs a @Inject(token) decorator. Explicit tokens survive circular imports, test setup, and runtime contracts more reliably than reflected metadata.

Explicit Constructor Tokens

Always use @Inject(...), including for concrete classes. Reflected constructor metadata exists as a fallback, but explicit tokens are clearer and more robust.

order_service.ts
import { Component, Inject, LoadAs } from "@noego/ioc";

@Component({ scope: LoadAs.Singleton })
export class OrderService {
  constructor(
    @Inject(PaymentGateway) private payment: PaymentGateway,
    @Inject(InventoryReader) private inventory: InventoryReader,
    @Inject(Notifier) private notify: Notifier,
  ) {}
}

Why Explicit Is Better

Circular imports: When files import each other, the JavaScript module system may not have finished evaluating one module by the time the other tries to read its metadata. Explicit tokens break the cycle.

Test registration: Registering a fake for PaymentGateway works because the test calls container.registerFunction(PaymentGateway, ...) — the same token the constructor uses.

Code clarity: A reader sees exactly which dependency each parameter represents without jumping to metadata configuration.

Avoid Implicit Construction

The example below relies on two implicit choices: default transient scope and reflected constructor metadata. Prefer the explicit form in NoEgo application code.

avoid.ts
// Avoid this — relies on implicit reflected constructor metadata.
@Component()
export class UserService {
  constructor(private users: UserRepository) {}
}

Injecting Abstract Tokens

When the contract is an abstract class, inject it the same way. The container resolves the registered concrete implementation.

checkout_service.ts
import { Component, Inject, LoadAs } from "@noego/ioc";

export abstract class PaymentGateway {
  abstract charge(amount: number): Promise<string>;
}

@Component({ scope: LoadAs.Singleton })
export class StripeGateway extends PaymentGateway {
  async charge(amount: number): Promise<string> {
    // Stripe implementation.
    return "ch_123";
  }
}

@Component({ scope: LoadAs.Singleton })
export class CheckoutService {
  constructor(
    @Inject(PaymentGateway) private gateway: PaymentGateway,
  ) {}
}

Rules

  • Always use @Inject(...) — do not rely on reflected metadata.
  • Pass the class or abstract class token, not a string.
  • Use abstract class tokens for swappable implementations.
  • Keep constructor injection only; avoid property injection patterns.
NoEgo

© 2025 NoEgo. All rights reserved.