TypeDI: TypeScript Dependency Injection
TypeDI is a dependency injection framework specifically designed for TypeScript and JavaScript applications. It enables the creation of loosely coupled, well-structured, and easily testable applications in both Node.js and browser environments. The current stable version is 0.10.0, with releases occurring periodically to introduce new features, improvements, and bug fixes, as indicated by the recent 0.9.x to 0.10.0 progression. Key differentiators include support for both property and constructor-based injection, management of singleton and transient services, and the ability to work with multiple DI containers within a single application. It heavily leverages TypeScript decorators and the `reflect-metadata` polyfill to achieve its injection capabilities, making proper configuration of `tsconfig.json` crucial for its operation.
Common errors
-
TypeError: Reflect.metadata is not a function
cause `reflect-metadata` was not imported, or not imported as the very first line of your application.fixEnsure `import 'reflect-metadata';` is the absolute first line in your main application file before any other imports. -
Error: Service 'MyService' was not found in the container. Make sure you have decorated it with @Service() and registered it.
cause The class intended for injection either lacks the `@Service()` decorator or the container cannot resolve its dependencies due to a misconfiguration or circular dependency.fixVerify that `MyService` has `@Service()` applied. Check `tsconfig.json` for `emitDecoratorMetadata` and `experimentalDecorators`. Inspect dependency graph for circular references. -
TS2304: Cannot find name 'Reflect'.
cause TypeScript compiler is unaware of the `Reflect` global object, likely because `reflect-metadata` is installed but not included in `tsconfig.json`'s `types` or `lib`.fixAdd `"reflect-metadata"` to the `types` array in your `tsconfig.json`'s `compilerOptions`, or ensure `"lib": ["esnext", "dom"]` (or similar) is present which often includes `esnext.reflect`.
Warnings
- breaking TypeDI versions prior to 0.6.0 had slightly different decorator application and API for registering services. While 0.10.0 is stable, always consult release notes for major version updates from older versions.
- gotcha The `reflect-metadata` package *must* be imported as the very first line of your application's entry file. Failing to do so will result in runtime errors related to missing metadata when decorators are processed.
- gotcha TypeDI relies on TypeScript's experimental decorators and metadata emission. Your `tsconfig.json` must include `"emitDecoratorMetadata": true` and `"experimentalDecorators": true` under `compilerOptions`.
- gotcha Circular dependencies can occur when Service A depends on Service B, and Service B also depends on Service A. TypeDI can sometimes resolve simple circular dependencies with constructor injection if not deeply nested, but complex cases can lead to runtime errors or `undefined` injections.
Install
-
npm install typedi -
yarn add typedi -
pnpm add typedi
Imports
- Container
const Container = require('typedi').Container;import { Container } from 'typedi'; - Service
import Service from 'typedi';
import { Service } from 'typedi'; - 'reflect-metadata'
require('reflect-metadata');import 'reflect-metadata';
Quickstart
import 'reflect-metadata';
import { Container, Service } from 'typedi';
// A service that will be injected
@Service()
class MailerService {
private readonly apiKey: string;
constructor() {
this.apiKey = process.env.MAILER_API_KEY ?? 'default-api-key';
}
sendMail(to: string, subject: string, body: string): void {
console.log(`Sending email to ${to} with subject '${subject}' via API Key: ${this.apiKey}`);
console.log(`Body: ${body}`);
}
}
// A service that depends on MailerService
@Service()
class UserService {
constructor(private mailer: MailerService) {}
registerUser(email: string, username: string): void {
console.log(`Registering user: ${username} (${email})`);
this.mailer.sendMail(email, 'Welcome!', `Hello ${username}, welcome to our service!`);
}
}
// Get an instance of UserService from the container, which will automatically inject MailerService
const userService = Container.get(UserService);
userService.registerUser('john.doe@example.com', 'JohnDoe');
// You can also get other services directly
const mailerService = Container.get(MailerService);
mailerService.sendMail('admin@example.com', 'System Alert', 'Server running fine!');