When writing Swingletree I came across InversifyJS, which enables us to use Dependency Injection in our TypeScript applications. The refactor-hammer was more than ready to be swung, as soon as I discovered that constructor injection is supported and almost everything is managed via annotations.

So.. how does it look like? Let’s take a look at an injectable class supporting constructor injection:

// omitted other imports for clarity
import { injectable } from "inversify";
import { inject } from "inversify";

@injectable()
class GithubWebhook {
	constructor(
		@inject(EventBus) eventBus: EventBus,
		@inject(ConfigurationService) configService: ConfigurationService
	) {
		this.eventBus = eventBus;
		this.configService = configService;
	}
  
  // ...
}

Everything needs to be strapped together using an inversify container, which takes care of the dependency injections and class instantiation:

import "reflect-metadata";

import { Container } from "inversify";

import { ConfigurationService } from "../configuration";
import EventBus from "../event-bus";
import GithubWebhook from "../github/github-webhook";


const container = new Container();

container.bind<EventBus>(EventBus).toSelf().inSingletonScope();
container.bind<ConfigurationService>(ConfigurationService).toSelf().inSingletonScope();
container.bind<GithubWebhook>(GithubWebhook).toSelf().inSingletonScope();

export default container;

Keep in mind that classes only get instantiated when they are a dependency of an other class. You can manually require them by using this:

container.get<CommitStatusSender>(CommitStatusSender);

Inversify has some very good examples, if you want to get into the details.