Skip to main content

Intro

Providers in Depinjionary

In Depinjionary, a Provider is a configuration object that describes how a dependency should be registered and resolved by the container. All dependencies in your application must be passed as providers.

Provider Interface

The Provider interface defines how a dependency is described:

interface Provider<T = any> {
provide: Token; // The identifier for the dependency
useClass?: Constructable<T>; // A class to instantiate
useValue?: T; // A fixed value
injectTokens?: Token[]; // Tokens for dependencies injected into the factory
useFactory?: (...args: any[]) => T; // A function to create the dependency
}

Tokens: Identifiers for Dependencies

A Token is used to uniquely identify a dependency in the container. It can be:

type Token = string | Symbol | Constructable;
  • String or Symbol: Use for lightweight, descriptive identifiers.
  • Constructable: Use a class as a token for type-safe resolution.

Passing Dependencies as Providers

When setting up your container, all dependencies must be included in the providers array. Here's an example:

import { ContainerResolver, ContainerInterface } from "depinjionary";
import { CalculatorService } from "./calculator.service";

const providers = [
{ provide: CalculatorService, useClass: CalculatorService }
];

const container: ContainerInterface = ContainerResolver.init(providers);
const calculator = await container.resolve<CalculatorService>(CalculatorService);
console.log(calculator.calculate(5, 10)); // Outputs: 15

Reflection-Based Resolution

If only the provide key is specified in a provider object, Depinjionary will use reflection to resolve the dependency. This approach works when the constructor does not depend on primitive types (like string, number, etc.):

const providers = [
{ provide: CalculatorService } // Reflection will instantiate CalculatorService
];

Important:
Avoid relying on reflection-based resolution if your dependency constructor has primitive parameters. Use explicit tokens or factories in such cases to ensure correct resolution.


Example: Full Setup

import { ContainerResolver, ContainerInterface } from "depinjionary";
import { CalculatorService } from "./calculator.service";
import { LoggerService } from "./logger.service";

const providers = [
{ provide: CalculatorService, useClass: CalculatorService },
{ provide: "Logger", useClass: LoggerService }
];

const container: ContainerInterface = ContainerResolver.init(providers);

const calculator = await container.resolve<CalculatorService>(CalculatorService);
const logger = await container.resolve<LoggerService>("Logger");

Key Notes

  • All dependencies must be passed to the providers array.
  • Use reflection-based resolution sparingly, and only when your constructor does not include primitive values.
  • Explicitly define how dependencies are resolved for more predictable behavior.