Skip to content

tsyringe

Developer UtilitiesDependency InjectionTypeScript

What it is

tsyringe is a lightweight dependency injection container for TypeScript from Microsoft, using decorators and constructor injection.

Mark classes @injectable and resolve them from the container. Interfaces need a token, because TypeScript interfaces do not exist at runtime.

Installation

npm install tsyringe reflect-metadata

Getting started

The smallest useful thing you can do with it, and what each part means.

Constructor injection
import 'reflect-metadata';
import { injectable, singleton, container } from 'tsyringe';

@singleton()
class Database { query(sql: string) { /* … */ } }

@injectable()
class BookService {
  constructor(private db: Database) {}   // resolved by type
  find(id: string) { return this.db.query('SELECT …'); }
}

const service = container.resolve(BookService);
@singleton keeps one instance for the process; @injectable creates a new one per resolve. The class type itself acts as the token.

Advanced usage

Where the library earns its place over a simpler alternative.

Injecting interfaces via tokens
interface Mailer { send(to: string, body: string): Promise<void>; }

// Interfaces vanish at runtime, so a token is required.
export const MAILER = Symbol('Mailer');

@injectable()
class NotificationService {
  constructor(@inject(MAILER) private mailer: Mailer) {}
}

container.register<Mailer>(MAILER, { useClass: SmtpMailer });

// Tests swap the implementation without touching the consumer.
const testContainer = container.createChildContainer();
testContainer.register<Mailer>(MAILER, { useValue: fakeMailer });
Child containers are the testing story: override one dependency while inheriting the rest, with no global state to reset between tests.

Errors and fixes

The failures you are most likely to hit, and what actually resolves them.

Cannot inject the dependency at position 0
The parameter is an interface or an unregistered type. Add an @inject token and register it.
TypeInfo not known for the class
The class lacks @injectable, or emitDecoratorMetadata is off in tsconfig.

Best practices

  • Import 'reflect-metadata' exactly once, at the very top of the entry point.
  • Use a Symbol token for every interface — type-based resolution cannot work for them.
  • Use child containers in tests instead of mutating the global container.
  • Resolve at the composition root only; calling container.resolve deep in the code is the service-locator anti-pattern.

Background

Why it exists, and what it was reacting to.

Built by Microsoft for teams wanting the testability of dependency injection without adopting a full framework such as NestJS or InversifyJS.