What is typedi?
TypeDI is a dependency injection tool for TypeScript and JavaScript. It allows you to manage the lifecycle and dependencies of your services and components in a clean and efficient way.
What are typedi's main functionalities?
Service Decorator
The `@Service` decorator is used to mark a class as a service that can be injected. The `Container.get` method is then used to retrieve an instance of the service.
```typescript
import { Service } from 'typedi';
@Service()
class ExampleService {
sayHello() {
return 'Hello, World!';
}
}
const exampleService = Container.get(ExampleService);
console.log(exampleService.sayHello()); // Output: Hello, World!
```
Inject Decorator
The `@Inject` decorator is used to inject a dependency into a class. In this example, `DependencyService` is injected into `MainService`.
```typescript
import { Service, Inject } from 'typedi';
@Service()
class DependencyService {
getValue() {
return 'Dependency Value';
}
}
@Service()
class MainService {
constructor(@Inject(() => DependencyService) private dependencyService: DependencyService) {}
getValue() {
return this.dependencyService.getValue();
}
}
const mainService = Container.get(MainService);
console.log(mainService.getValue()); // Output: Dependency Value
```
Container
The `Container` class is used to manage the lifecycle of services. You can manually set and get instances of services using `Container.set` and `Container.get`.
```typescript
import { Container } from 'typedi';
class ExampleService {
sayHello() {
return 'Hello, World!';
}
}
Container.set(ExampleService, new ExampleService());
const exampleService = Container.get(ExampleService);
console.log(exampleService.sayHello()); // Output: Hello, World!
```
Other packages similar to typedi
inversify
Inversify is a powerful and flexible inversion of control (IoC) container for JavaScript and TypeScript. It provides a similar feature set to TypeDI, including decorators for services and dependency injection. Inversify is known for its extensive documentation and strong community support.
tsyringe
TSyringe is a lightweight dependency injection container for TypeScript. It offers a similar decorator-based API for defining and injecting services. TSyringe is designed to be simple and easy to use, making it a good choice for smaller projects or those new to dependency injection.
awilix
Awilix is a dependency injection container for JavaScript and TypeScript that focuses on developer experience and ease of use. It provides a fluent API for defining and resolving dependencies, and supports both class-based and function-based services. Awilix is known for its flexibility and ease of integration with various frameworks.
TypeDI
Dependency injection tool for Typescript.
Installation
-
Install module:
npm install typedi --save
-
Use typings to install all required definition dependencies.
typings install
-
ES6 features are used, so you may want to install es6-shim too. You also
need to install reflect-metadata package.
npm install es6-shim --save
npm install reflect-metadata --save
if you are building nodejs app, you may want to require("es6-shim");
and require("reflect-metadata")
in your app.
Usage
If you simply want to use a container:
import {Container} from "typedi";
class SomeClass {
someMethod() {
}
}
let someClass = Container.get(SomeClass);
someClass.someMethod();
If you want to inject other classes into your service you can do:
import {Container, Inject} from "typedi";
class BeanFactory {
create() {
}
}
class SugarFactory {
create() {
}
}
class WaterFactory {
create() {
}
}
class CoffeeMaker {
@Inject()
beanFactory: BeanFactory;
@Inject()
sugarFactory: SugarFactory;
@Inject()
waterFactory: WaterFactory;
make() {
this.beanFactory.create();
this.sugarFactory.create();
this.waterFactory.create();
}
}
let coffeeMaker = Container.get(CoffeeMaker);
coffeeMaker.make();
If you want to use constructor injection:
import {Container, Service} from "typedi";
class BeanFactory {
create() {
}
}
class SugarFactory {
create() {
}
}
class WaterFactory {
create() {
}
}
@Service()
class CoffeeMaker {
private beanFactory: BeanFactory;
private sugarFactory: SugarFactory;
private waterFactory: WaterFactory;
constructor(beanFactory: BeanFactory, sugarFactory: SugarFactory, waterFactory: WaterFactory) {
this.beanFactory = beanFactory;
this.sugarFactory = sugarFactory;
this.waterFactory = waterFactory;
}
make() {
this.beanFactory.create();
this.sugarFactory.create();
this.waterFactory.create();
}
}
let coffeeMaker = Container.get(CoffeeMaker);
coffeeMaker.make();
note: Your classes may not to have @Service
decorator to use it with Container, however its recommended to add
@Service
decorator to all classes you are using with container, especially if you class injects other
services
Also you can inject a modules that you want to require
:
import {Container, Service, Require} from "typedi";
@Service()
class CoffeeMaker {
private gulp: any;
constructor(@Require("gulp") gulp: any) {
this.gulp = gulp;
}
make() {
console.log(this.gulp);
}
}
let coffeeMaker = Container.get(CoffeeMaker);
coffeeMaker.make();
Named services
You can use a named services. In this case you can use interface-based services.
import {Container, Service, Inject} from "typedi";
interface Factory {
create(): void;
}
@Service("bean.factory")
class BeanFactory implements Factory {
create() {
}
}
@Service("sugar.factory")
class SugarFactory implements Factory {
create() {
}
}
@Service("water.factory")
class WaterFactory implements Factory {
create() {
}
}
@Service("coffee.maker")
class CoffeeMaker {
beanFactory: Factory;
sugarFactory: Factory;
@Inject("water.factory")
waterFactory: Factory;
constructor(@Inject("bean.factory") beanFactory: BeanFactory,
@Inject("sugar.factory") sugarFactory: SugarFactory) {
this.beanFactory = beanFactory;
this.sugarFactory = sugarFactory;
}
make() {
this.beanFactory.create();
this.sugarFactory.create();
this.waterFactory.create();
}
}
let coffeeMaker = Container.get<CoffeeMaker>("coffee.maker");
coffeeMaker.make();
Providing values to the container
If you are writing unit tests for you class, you may want to provide fakes to your classes. You can use set
or
provide
methods of the container:
Container.set(CoffeeMaker, new FakeCoffeeMaker());
Container.provide([
{ name: "bean.factory", type: BeanFactory, value: new FakeBeanFactory() },
{ name: "sugar.factory", type: SugarFactory, value: new FakeSugarFactory() },
{ name: "water.factory", type: WaterFactory, value: new FakeWaterFactory() }
]);
Problem with circular references
There is a known issue in language that it can't handle circular references. For example:
@Service()
export class Car {
@Inject()
engine: Engine;
}
@Service()
export class Engine {
@Inject()
car: Car;
}
This code will not work, because Engine has a reference to Car, and Car has a reference to Engine.
One of them will be undefined and it will cause an errors. To fix them you need to specify a type in a function like this:
@Service()
export class Car {
@Inject(type => Engine)
engine: Engine;
}
@Service()
export class Engine {
@Inject(type => Car)
car: Car;
}
And that's all. Same for injects for constructor injection.
Inherited injections
Inherited injections are supported as well. In order to use them you must mark inherited class as a @Service.
For example:
@Service()
export abstract class Car {
@Inject(type => Engine)
engine: Engine;
}
@Service()
export class Bus extends Car {
}
Samples
Take a look on samples in ./sample for more examples of
usages.