didi: Dependency Injection Container
didi is a lightweight, battle-tested Inversion of Control (IoC) container for JavaScript and TypeScript, primarily used for dependency injection. It allows decoupling component declaration from instantiation by defining modules that declare components by name and specify how they are provided (e.g., as types, factories, or static values). The library then instantiates components on demand, transitively resolves their dependencies, and caches instances for reuse. Currently at stable version 11.0.0, didi maintains a moderate release cadence, largely driven by maintenance and dependency updates, with major version bumps often related to module system changes (e.g., ESM-only). Its key differentiators include its minimal footprint, focus on core DI patterns, and proven use in mature projects like Karma and diagram-js, making it suitable for applications requiring robust, explicit dependency management.
Common errors
-
TypeError: Injector is not a constructor
cause Attempting to use `require('didi')` after upgrading to v11.0.0, which is ESM-only.fixChange `const { Injector } = require('didi');` to `import { Injector } from 'didi';` and ensure your project is configured for ES modules. -
Error: No provider for 'dependencyName'! (Resolving: dependencyName)
cause A component attempts to inject a dependency that has not been registered in any of the modules provided to the Injector.fixVerify that 'dependencyName' is correctly defined in one of your didi modules using 'type', 'factory', or 'value' declaration. Check for typos in both the module definition and the injection point. -
TS2345: Argument of type 'string' is not assignable to parameter of type 'any[]'.
cause Incorrect type signature when defining a didi module in TypeScript, often due to not using the array notation for providers.fixEnsure module definitions conform to `[ 'type' | 'factory' | 'value', ... ]` array structure. For example, `{'myService': ['type', MyService]}`.
Warnings
- breaking Version 11.0.0 drops support for CommonJS distribution. The package is now an ES module only.
- breaking Node.js engine requirement has been updated to `^20.12`.
- gotcha didi relies on argument names, function comments, or explicit `$inject` annotations for dependency resolution. Minification without proper configuration can break injection if not using `$inject` or array notation.
Install
-
npm install didi -
yarn add didi -
pnpm add didi
Imports
- Injector
const Injector = require('didi');import { Injector } from 'didi'; - Module
type Module = { [key: string]: any[] }; - TypedDeclaration
import TypedDeclaration from 'didi';
import { TypedDeclaration } from 'didi';
Quickstart
import { Injector } from 'didi';
interface Engine {
start(): void;
}
class PetrolEngine implements Engine {
private power: number;
constructor(power: number) {
this.power = power;
}
start(): void {
console.log(`Starting petrol engine with ${this.power}hp`);
}
}
class ElectricEngine implements Engine {
private voltage: number;
constructor(voltage: number) {
this.voltage = voltage;
}
start(): void {
console.log(`Starting electric engine with ${this.voltage}V`);
}
}
class Car {
static $inject = ['engine', 'licensePlate'];
private engine: Engine;
private licensePlate: string;
constructor(engine: Engine, licensePlate: string) {
this.engine = engine;
this.licensePlate = licensePlate;
}
start() {
console.log(`Car with license ${this.licensePlate} is starting.`);
this.engine.start();
}
}
const carModule = {
'engine': ['type', PetrolEngine],
'power': ['value', 150],
'licensePlate': ['value', 'XYZ-123']
};
const electricCarModule = {
'engine': ['type', ElectricEngine],
'voltage': ['value', 400],
'licensePlate': ['value', 'EV-456']
};
const petrolInjector = new Injector([carModule]);
const petrolCar = petrolInjector.get<Car>('car');
petrolCar.start();
const electricInjector = new Injector([electricCarModule]);
const electricCar = electricInjector.get<Car>('car');
electricCar.start();
// Example of invoking a function with injected dependencies
petrolInjector.invoke(function(car: Car) {
console.log('\nInvoking a function with injected car:');
car.start();
});