TSyringe Dependency Injection Container

4.10.0 · active · verified Sun Apr 19

TSyringe is a lightweight dependency injection (DI) container for JavaScript and TypeScript, primarily focusing on constructor injection. Currently at stable version 4.10.0, it is actively maintained and backed by Microsoft. Its release cadence appears to be feature-driven, with recent updates (v4.4.0) adding interception and transformation capabilities. Key differentiators include its strong integration with TypeScript decorators (`@injectable`, `@singleton`, `@inject`), its ability to handle complex dependency graphs including circular dependencies with a `delay` helper, and its provision for both class-based and interface-based injection using tokens. It requires `reflect-metadata` for decorator-based type reflection and specific `tsconfig.json` settings.

Common errors

Warnings

Install

Imports

Quickstart

Demonstrates basic setup, interface-based dependency injection, and singleton registration.

import "reflect-metadata"; // Must be imported ONCE at the very top of your application entry point
import { container, injectable, inject, singleton } from "tsyringe";

// 1. Define an interface for abstraction
interface IDatabaseService {
  connect(): void;
  getData(query: string): string;
}

// 2. Implement the interface and mark as injectable
@injectable()
class RealDatabaseService implements IDatabaseService {
  connect() {
    console.log("Connected to a real database.");
  }
  getData(query: string): string {
    return `Data for: ${query} from RealDatabase`;
  }
}

// 3. Define a service that depends on IDatabaseService
@injectable()
class DataProcessorService {
  constructor(@inject("IDatabaseService") private dbService: IDatabaseService) {
    this.dbService.connect();
  }

  processUserData(userId: string): string {
    const query = `SELECT * FROM users WHERE id = ${userId}`;
    return `Processing user data: ${this.dbService.getData(query)}`;
  }
}

// 4. Register the interface with its implementation in the container
container.register<IDatabaseService>("IDatabaseService", {
  useClass: RealDatabaseService,
});

// 5. Resolve the DataProcessorService
const dataProcessor = container.resolve(DataProcessorService);
console.log(dataProcessor.processUserData("123"));

// 6. Example of a singleton service
@singleton()
class AppConfig {
  public readonly API_KEY = process.env.API_KEY ?? 'default-api-key';
  constructor() {
    console.log("AppConfig initialized (should only happen once for singleton).");
  }
}

const config1 = container.resolve(AppConfig);
const config2 = container.resolve(AppConfig);
console.log(`API Key: ${config1.API_KEY}`);
console.log(`Are config instances identical? ${config1 === config2}`);

/*
  NOTE: Ensure your tsconfig.json includes:
  {
    "compilerOptions": {
      "experimentalDecorators": true,
      "emitDecoratorMetadata": true
    }
  }
*/

view raw JSON →