What is tsyringe?
tsyringe is a lightweight dependency injection container for TypeScript and JavaScript applications. It helps manage class dependencies and promotes the use of the Inversion of Control (IoC) principle, making it easier to write modular, testable, and maintainable code.
What are tsyringe's main functionalities?
Dependency Injection
This feature allows you to inject dependencies into classes. In the example, `Foo` is injected into `Bar`, and `Bar` can use `Foo`'s methods.
const { container, injectable, inject } = require('tsyringe');
@injectable()
class Foo {
log() {
console.log('Foo');
}
}
@injectable()
class Bar {
constructor(@inject(Foo) foo) {
this.foo = foo;
}
log() {
this.foo.log();
console.log('Bar');
}
}
const bar = container.resolve(Bar);
bar.log();
Singleton Registration
This feature allows you to register a class as a singleton, ensuring that only one instance of the class is created and shared across the application.
const { container, singleton } = require('tsyringe');
@singleton()
class SingletonService {
constructor() {
this.id = Math.random();
}
}
const instance1 = container.resolve(SingletonService);
const instance2 = container.resolve(SingletonService);
console.log(instance1.id === instance2.id); // true
Scoped Registration
This feature allows you to register a class with a scoped lifecycle, meaning a new instance is created for each container scope.
const { container, scoped, Lifecycle } = require('tsyringe');
@scoped(Lifecycle.ContainerScoped)
class ScopedService {
constructor() {
this.id = Math.random();
}
}
const childContainer = container.createChildContainer();
const instance1 = childContainer.resolve(ScopedService);
const instance2 = childContainer.resolve(ScopedService);
console.log(instance1.id === instance2.id); // true
const instance3 = container.resolve(ScopedService);
console.log(instance1.id === instance3.id); // false
Custom Providers
This feature allows you to register custom providers for dependencies, giving you more control over how dependencies are resolved.
const { container, inject, injectable } = require('tsyringe');
class CustomProvider {
get() {
return 'Custom Value';
}
}
container.register('CustomProvider', { useClass: CustomProvider });
@injectable()
class Consumer {
constructor(@inject('CustomProvider') provider) {
this.provider = provider;
}
log() {
console.log(this.provider.get());
}
}
const consumer = container.resolve(Consumer);
consumer.log(); // Custom Value
Other packages similar to tsyringe
inversify
Inversify is a powerful and flexible IoC container for TypeScript and JavaScript. It offers more advanced features like middleware, multi-injection, and context-based bindings. However, it has a steeper learning curve compared to tsyringe.
typedi
TypeDI is another dependency injection tool for TypeScript. It is similar to tsyringe in terms of ease of use and simplicity but offers additional features like service decorators and parameter decorators. It is slightly more feature-rich but also a bit more complex.
awilix
Awilix is a dependency injection container for JavaScript and TypeScript with a focus on developer experience. It offers features like lifecycle management and resolution modes. It is more flexible and configurable but can be more verbose compared to tsyringe.
TSyringe
A lightweight dependency injection container for TypeScript/TypeScript for
constructor injection.
Installation
npm install --save tsyringe
Modify your tsconfig.json
to include the following settings
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Usage
Example without interfaces
Since classes have type information at runtime, we can resolve them without any
extra information. If a particular class isn't registered with the container then
a new instance will be resolved each time.
export class Foo {}
import {Foo} from "./Foo";
import {decorators} from "tsyringe";
const {injectable} = decorators;
@injectable()
export class Bar {
constructor(public myFoo: Foo) {}
}
import {container} from "tsyringe";
import {Bar} from "./Bar";
const myBar = container.resolve(Bar);
Example with interfaces
Interfaces don't have type information at runtime, so we need to decorate them
with @inject(...)
so the container knows how to resolve them.
export interface SuperService {
}
import {SuperService} from "./SuperService";
export class TestService implements SuperService {
}
import {decorators} from "tsyringe";
const {injectable, inject} = decorators;
@injectable()
export class Client {
constructor(@inject("SuperService") private service: SuperService) {}
}
import {Client} from "./Client";
import {TestService} from "./TestService";
import {container} from "tsyringe";
container.register({
token: "SuperService",
useClass: TestService
});
const client = container.resolve(Client);
Contributing
This project welcomes contributions and suggestions. Most contributions require you to agree to a
Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us
the rights to use your contribution. For details, visit https://cla.microsoft.com.
When you submit a pull request, a CLA-bot will automatically determine whether you need to provide
a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the instructions
provided by the bot. You will only need to do this once across all repos using our CLA.
This project has adopted the Microsoft Open Source Code of Conduct.
For more information see the Code of Conduct FAQ or
contact opencode@microsoft.com with any additional questions or comments.