InversifyJS: Powerful IoC Container for TypeScript
InversifyJS is a lightweight yet extremely powerful IoC (Inversion of Control) container for TypeScript and JavaScript applications. It implements the Dependency Injection (DI) pattern, allowing developers to create loosely coupled, testable, and scalable systems. The library actively leverages TypeScript features (decorators, types) to ensure strict typing and development convenience.
InversifyJS is ideal for server-side Node.js applications as well as client-side projects with frameworks like Angular, React, or Vue. In this overview, we'll break down the key concepts and show you how to get started quickly.
Core Concepts of InversifyJS
Before writing code, it's important to understand the three main entities of the library:
- Container — the central object that manages all dependencies. You register abstractions and their implementations in it.
- Service Identifiers — unique keys by which the container finds the required implementation. They can be strings, symbols, or classes.
- Decorators —
@injectable()and@inject()— tell TypeScript that a class can be injected and what dependencies it needs.
Installation and Setup
First, install InversifyJS and its typings dependencies:
npm install inversify reflect-metadata --savenpm install --save-dev @types/nodeThen add two required options to tsconfig.json:
{ "compilerOptions": { "emitDecoratorMetadata": true, "experimentalDecorators": true, "types": ["reflect-metadata"] }}After that, import reflect-metadata in your main application file (usually index.ts or main.ts):
import "reflect-metadata";First Example: Simple Service
Let's create an interface and its implementation. Note the @injectable() decorator:
// interfaces/IWarrior.tsexport interface IWarrior { fight(): string;}
// entities/Warrior.tsimport { injectable } from "inversify";
@injectable()export class Warrior implements IWarrior { fight(): string { return "Warrior attacks with sword!"; }}Now register this binding in the container and get an instance:
import { Container } from "inversify";import { IWarrior } from "./interfaces/IWarrior";import { Warrior } from "./entities/Warrior";
const container = new Container();
// Register the abstraction with its implementationcontainer.bind<IWarrior>("IWarrior").to(Warrior);
// Get an instanceconst warrior = container.get<IWarrior>("IWarrior");console.log(warrior.fight()); // "Warrior attacks with sword!"Dependency Injection via Constructor
The true power of DI is revealed when one class depends on another. Use the @inject() decorator:
// interfaces/IWeapon.tsexport interface IWeapon { use(): string;}
// entities/Sword.tsimport { injectable } from "inversify";
@injectable()export class Sword implements IWeapon { use(): string { return "Swing sword!"; }}
// entities/Knight.tsimport { injectable, inject } from "inversify";import { IWarrior } from "../interfaces/IWarrior";import { IWeapon } from "../interfaces/IWeapon";
@injectable()export class Knight implements IWarrior { private _weapon: IWeapon;
constructor(@inject("IWeapon") weapon: IWeapon) { this._weapon = weapon; }
fight(): string { return this._weapon.use() + " The knight fights!"; }}Regist
Also in library
CLI frameworks for Java: overview and comparison of the best tools
Data Validation in Java: Overview of Libraries and Approaches
Go (Golang) Task Scheduler: A Guide to Implementing Cron Jobs
Monitoring Swift Applications: Libraries and Tools for iOS/macOS