
Security News
vlt Launches "reproduce": A New Tool Challenging the Limits of Package Provenance
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
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.
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!
```
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 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 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.
Simple yet powerful dependency injection tool for TypeScript.
Install module:
npm install typedi --save
You also need to install reflect-metadata package.
npm install reflect-metadata --save
and import it somewhere in the global place of your app (for example in app.ts
):
import "reflect-metadata";
You may need to install node typings:
npm install @types/node --save
Also make sure you are using TypeScript compiler version > 2.1
and you have enabled following settings in tsconfig.json
:
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
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, Service} from "typedi";
@Service()
class BeanFactory {
create() {
}
}
@Service()
class SugarFactory {
create() {
}
}
@Service()
class WaterFactory {
create() {
}
}
@Service()
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";
@Service()
class BeanFactory {
create() {
}
}
@Service()
class SugarFactory {
create() {
}
}
@Service()
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, because without@Service
decorator applied constructor injection may not work properly in your classes.
Also you can inject a modules that you want to require
:
import {Container, Service, Require} from "typedi";
@Service()
class CoffeeMaker {
private logger: any; // you can use type if you have definition for this package
constructor(@Require("logger") logger: any) {
this.logger = logger; // the same if you do this.logger = require("logger")
}
make() {
console.log(this.logger); // here you get console.logged logger package =)
}
}
let coffeeMaker = Container.get(CoffeeMaker);
coffeeMaker.make();
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();
You can use a services with a Token
instead of name or target class.
In this case you can use type safe interface-based services.
import {Container, Service, Inject, Token} from "typedi";
export interface Factory {
create(): void;
}
export const FactoryService = new Token<Factory>();
@Service(FactoryService)
export class BeanFactory implements Factory {
create() {
}
}
@Service()
export class CoffeeMaker {
private factory: Factory;
constructor(@Inject(FactoryService) factory: Factory) {
this.factory = factory;
}
make() {
this.factory.create();
}
}
let coffeeMaker = Container.get(CoffeeMaker);
coffeeMaker.make();
let factory = Container.get(FactoryService);
factory.create();
You can create your services with the container using factory functions.
This way, service instance will be created by calling your factory function instead of instantiating a class directly.
import {Container, Service} from "typedi";
function createCar() {
return new Car("V8");
}
@Service({ factory: createCar })
class Car {
constructor (public engineType: string) {
}
}
// Getting service from the container.
// Service will be created by calling the specified factory function.
const car = Container.get(Car);
console.log(car.engineType); // > "V8"
You can also create your services using factory classes.
This way, service instance will be created by calling given factory service's method factory instead of instantiating a class directly.
import {Container, Service} from "typedi";
@Service()
class CarFactory {
constructor(public logger: LoggerService) {
}
create() {
return new Car("BMW", this.logger);
}
}
@Service({ factory: [CarFactory, "create"] })
class Car {
constructor(public model: string, public logger: LoggerInterface) {
}
}
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());
// or
Container.provide([
{ id: "bean.factory", value: new FakeBeanFactory() },
{ id: "sugar.factory", value: new FakeSugarFactory() },
{ id: "water.factory", value: new FakeWaterFactory() }
]);
There is a known issue in language that it can't handle circular references. For example:
// Car.ts
@Service()
export class Car {
@Inject()
engine: Engine;
}
// Engine.ts
@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 cause errors. To fix them you need to specify a type in a function this way:
// Car.ts
@Service()
export class Car {
@Inject(type => Engine)
engine: Engine;
}
// Engine.ts
@Service()
export class Engine {
@Inject(type => Car)
car: Car;
}
And that's all. Same for injects for constructor injection.
Inherited injections are supported as well. In order to use them you must mark inherited class as a @Service. For example:
// Car.ts
@Service()
export abstract class Car {
@Inject(type => Engine)
engine: Engine;
}
// Engine.ts
@Service()
export class Bus extends Car {
// you can call this.engine in this class
}
You can create your own decorators which will inject your given values for your service dependencies. For example:
// Logger.ts
export function Logger() {
return function(object: Object, propertyName: string, index?: number) {
const logger = new ConsoleLogger();
Container.registerHandler({ object, propertyName, index, value: () => logger });
};
}
// LoggerInterface.ts
export interface LoggerInterface {
log(message: string): void;
}
// ConsoleLogger.ts
import {LoggerInterface} from "./LoggerInterface";
export class ConsoleLogger implements LoggerInterface {
log(message: string) {
console.log(message);
}
}
// UserRepository.ts
@Service()
export class UserRepository {
constructor(@Logger() private logger: LoggerInterface) {
}
save(user: User) {
this.logger.log(`user ${user.firstName} ${user.secondName} has been saved.`);
}
}
If you need to remove registered service from container simply use Container.remove(...)
method.
Also you can completely reset the container by calling Container.reset()
method.
This will effectively remove all registered services from the container.
Take a look on samples in ./sample for more examples of usages.
FAQs
Dependency injection for TypeScript.
The npm package typedi receives a total of 145,822 weekly downloads. As such, typedi popularity was classified as popular.
We found that typedi demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
vlt's new "reproduce" tool verifies npm packages against their source code, outperforming traditional provenance adoption in the JavaScript ecosystem.
Research
Security News
Socket researchers uncovered a malicious PyPI package exploiting Deezer’s API to enable coordinated music piracy through API abuse and C2 server control.
Research
The Socket Research Team discovered a malicious npm package, '@ton-wallet/create', stealing cryptocurrency wallet keys from developers and users in the TON ecosystem.