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.