Skip to content

InversifyJS

Developer UtilitiesDependency InjectionTypeScript

What it is

InversifyJS is a full-featured inversion of control container for TypeScript, with decorators, scopes, tagged bindings and middleware.

Bind interfaces to implementations in a container using symbol identifiers, then resolve the composition root. Scopes control instance lifetime.

Installation

npm install inversify reflect-metadata

Getting started

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

Bindings and injection
import 'reflect-metadata';
import { Container, injectable, inject } from 'inversify';

const TYPES = { Mailer: Symbol.for('Mailer'), Store: Symbol.for('Store') };

@injectable()
class SmtpMailer implements Mailer { /* … */ }

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

const container = new Container();
container.bind<Mailer>(TYPES.Mailer).to(SmtpMailer).inSingletonScope();
container.bind(NotificationService).toSelf();

const service = container.get(NotificationService);
Symbol.for identifiers avoid collisions and survive minification, unlike string tokens. Scope decides whether one instance is shared or created per resolve.

Advanced usage

Where the library earns its place over a simpler alternative.

Conditional bindings and test overrides
// Pick an implementation based on where it is being injected.
container.bind<Logger>(TYPES.Logger)
  .to(FileLogger)
  .whenInjectedInto(BatchProcessor);

container.bind<Logger>(TYPES.Logger)
  .to(ConsoleLogger)
  .when((request) => !request.parentRequest);

// Tests: snapshot, override, restore.
container.snapshot();
container.rebind<Mailer>(TYPES.Mailer).toConstantValue(fakeMailer);
// … run the test …
container.restore();
snapshot/restore is the cleanest testing pattern here — override a binding for one test and roll back without rebuilding the container.

Errors and fixes

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

No matching bindings found for serviceIdentifier
The symbol was never bound, or a different symbol instance was used. Symbol.for returns the same symbol for a given key; Symbol() does not.
Missing required @injectable annotation
Every class the container constructs needs the decorator, including ones bound with toSelf.

Best practices

  • Use Symbol.for for identifiers so they are collision-free and minification-safe.
  • Import reflect-metadata once, first, at the entry point.
  • Resolve only at the composition root; resolving deep in the code is a service locator.
  • Prefer tsyringe if you only need basic constructor injection — Inversify is considerably heavier.

Background

Why it exists, and what it was reacting to.

InversifyJS predates most TypeScript DI containers and remains the most feature-complete, offering contextual and conditional bindings that lighter containers do not.