Quick Start

Installation

npm i -S dainty-di

Setup

Dainty DI is implemented based on legacy decorators (before TypeScript 5.0), you need to use it with TypeScript and make sure that the relevant configurations are set correctly in tsconfig.json below:

{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true
  }
}

Basic Usage

Directly use @Provide decorator to declare a class as a dependency provider:

import { Provide } from 'dainty-di';

@Provide()
export class Foo {}

Inject the provider into the dependent class (it also needs to be a provider):

import { Provide, Inject } from 'dainty-di';
import { Foo } from './foo';

@Provide()
class Bar {
  constructor(private foo: Foo) {}

  // You can also declare dependencies on properties
  // instead of on constructor arguments
  @Inject()
  private foo: Foo;
}

Get the instance of the specified dependency provider:

import { DIUtility } from 'dainty-di';
import { Bar } from './bar';

const bar = DIUtility.getDependency(Bar);

TIP

When you get a dependency (most of the time it's probably an instance of the specified class) from DI container, the container recursively resolves all dependencies and their own declared dependencies then instantiates them correctly. It's driven by InversifyJSopen in new window.

Configure the Provider

Identifier

By default, the identifier of the provider is the decorated class itself, but you can also customize it:

import { Provide, Inject } from 'dainty-di';

@Provide({ id: 'foo' })
export class Foo {}

export class Bar {
  constructor(@Inject('foo') private foo: Foo) {}
}

You can use a class, string, or symbol as a dependency identifier. When an identifier is not explicitly specified, Dainty DI automatically infers the identifier by default based on the type (only classes are supported).

If you specify a custom identifier for the provider, you must use the same identifier when injecting, otherwise the dependency may not be found.

Scope

By default, a dependency provider is scoped as a singleton, meaning that all instances that depend on it acquire the same reference to it. At some point, you may need to configure the scope so that it generates a brand new instance each time it is requested. You can configure the scope option to transient:

import { Provide, ProviderScope } from 'dainty-di';

@Provide({ scope: ProviderScope.Transient })
export class Foo {}

Condition

If your dependency needs to be provided conditionally, you can use the built-in conditional option condition. It is convenient to change the dependency provider based on an environment variable or expression:

import { Provide, Inject } from 'dainty-di';

// The following provider will only be provided
// when the NODE_ENV environment variable is set to 'development'
@Provide({ id: 'foo', condition: 'development' })
export class FooDev {}

// You can also use a boolean expression
// It will be resolved the first time this dependency is imported
@Provide({ id: 'foo', condition: process.env.NODE_ENV === 'production' })
export class FooProd {}

export class Bar {
  constructor(@Inject('foo') private foo: Foo) {}
}

Overriding

If you need to override a dependency provider, you can use the built-in override option. In general, this is useful for overriding configurations based on conditions:

import { Provide, Inject } from 'dainty-di';

@Provide()
export class DBConfig {
  public host = '127.0.0.1';
  public port = 3306;
  public pass = 'test';
}

// Override the provider when condition is met
@Provide({ id: DBConfig, condition: 'production', override: true })
export class DBConfigProd extends DBConfig {
  public pass = 'another_pass';
}

export class DB {
  constructor(private config: DBConfig) {}
}

Provide dependencies imperatively

At some point, you may need to provide dependencies dynamically, such as classes or values that are initialized based on conditions. You can use the imperative interface bindProvider to manually provide dependencies to the DI container:

import { DIUtility, ProviderType } from 'dainty-di';

DIUtility.provideValue({
  id: Symbol.for('answer'),
  value: 42,
});

DIUtility.getDependency(Symbol.for('answer')); // 42