CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-inversify

A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

decorators.mddocs/

Decorators and Annotations

InversifyJS provides a comprehensive set of TypeScript decorators for marking classes as injectable and specifying dependency injection requirements. These decorators work with TypeScript's metadata reflection system to enable automatic dependency resolution.

Core Decorators

@injectable

Marks a class as available for dependency injection. This decorator is required on all classes that will be injected or serve as injection targets.

function injectable<T = {}>(target: Newable<T>): void;
@injectable()
class Warrior {
  fight() {
    return "Fighting!";
  }
}

@injectable()
class Ninja extends Warrior {
  private weapon: IWeapon;
  
  constructor(@inject("Weapon") weapon: IWeapon) {
    super();
    this.weapon = weapon;
  }
}

@inject

Specifies which service should be injected into a constructor parameter or class property.

function inject(serviceIdentifier: ServiceIdentifier): ParameterDecorator | PropertyDecorator;

Constructor Injection

@injectable()
class Ninja {
  constructor(
    @inject("Weapon") private weapon: IWeapon,
    @inject("Armor") private armor: IArmor
  ) {}
}

Property Injection

@injectable()
class Ninja {
  @inject("Weapon")
  private weapon!: IWeapon;
  
  @inject("Armor")
  public armor!: IArmor;
}

@multiInject

Injects an array of all services registered for a service identifier.

function multiInject(serviceIdentifier: ServiceIdentifier): ParameterDecorator | PropertyDecorator;
@injectable()
class Ninja {
  constructor(
    @multiInject("Weapon") private weapons: IWeapon[]
  ) {}
}

// Usage with property injection
@injectable()
class Warrior {
  @multiInject("Skill")
  private skills!: ISkill[];
}

Conditional Decorators

@named

Associates a name with an injection to enable conditional binding resolution.

function named(name: string): ParameterDecorator | PropertyDecorator;
@injectable()
class Ninja {
  constructor(
    @inject("Weapon") @named("primary") private primaryWeapon: IWeapon,
    @inject("Weapon") @named("secondary") private secondaryWeapon: IWeapon
  ) {}
}

// Binding configuration
container.bind<IWeapon>("Weapon").to(Katana).whenTargetNamed("primary");
container.bind<IWeapon>("Weapon").to(Shuriken).whenTargetNamed("secondary");

@tagged

Associates key-value metadata with an injection point for conditional resolution.

function tagged(key: string, value: any): ParameterDecorator | PropertyDecorator;
@injectable()
class Ninja {
  constructor(
    @inject("Weapon") @tagged("type", "melee") private meleeWeapon: IWeapon,
    @inject("Weapon") @tagged("type", "ranged") private rangedWeapon: IWeapon,
    @inject("Weapon") @tagged("power", "high") private powerWeapon: IWeapon
  ) {}
}

// Binding configuration
container.bind<IWeapon>("Weapon").to(Katana).whenTargetTagged("type", "melee");
container.bind<IWeapon>("Weapon").to(Bow).whenTargetTagged("type", "ranged");
container.bind<IWeapon>("Weapon").to(FireSword).whenTargetTagged("power", "high");

@optional

Marks a dependency as optional, preventing injection errors if no binding is found.

function optional(): ParameterDecorator | PropertyDecorator;
@injectable()
class Ninja {
  constructor(
    @inject("Weapon") private weapon: IWeapon,
    @inject("Shield") @optional() private shield?: IShield,
    @inject("Logger") @optional() private logger?: ILogger
  ) {}
}

// Property injection with optional
@injectable()
class Warrior {
  @inject("Weapon")
  private weapon!: IWeapon;
  
  @inject("Shield") @optional()
  private shield?: IShield;
}

Inheritance Decorators

@injectFromBase

Inherits injection metadata from the base class for the same parameter position.

function injectFromBase(): ParameterDecorator;
@injectable()
class BaseWarrior {
  constructor(@inject("Weapon") protected weapon: IWeapon) {}
}

@injectable()
class Ninja extends BaseWarrior {
  constructor(
    @injectFromBase() weapon: IWeapon,
    @inject("Armor") private armor: IArmor
  ) {
    super(weapon);
  }
}

@injectFromHierarchy

Inherits injection metadata from any class in the inheritance hierarchy.

function injectFromHierarchy(): ParameterDecorator;
@injectable()
class BaseEntity {
  constructor(@inject("Logger") protected logger: ILogger) {}
}

@injectable()
class BaseWarrior extends BaseEntity {
  constructor(
    @injectFromHierarchy() logger: ILogger,
    @inject("Weapon") protected weapon: IWeapon
  ) {
    super(logger);
  }
}

@injectable()
class Ninja extends BaseWarrior {
  constructor(
    @injectFromHierarchy() logger: ILogger,
    @injectFromHierarchy() weapon: IWeapon,
    @inject("Armor") private armor: IArmor
  ) {
    super(logger, weapon);
  }
}

@unmanaged

Excludes a constructor parameter from dependency injection, requiring manual value provision.

function unmanaged(): ParameterDecorator;
@injectable()
class Ninja {
  constructor(
    @inject("Weapon") private weapon: IWeapon,
    @unmanaged() private name: string,
    @unmanaged() private level: number
  ) {}
}

// Manual instantiation required for unmanaged parameters
const ninja = new Ninja(weapon, "Hanzo", 10);

Lifecycle Decorators

@postConstruct

Marks a method to be called after the object is fully constructed and all dependencies are injected.

function postConstruct(target: any, propertyKey: string): void;
@injectable()
class Ninja {
  @inject("Weapon") private weapon!: IWeapon;
  @inject("Armor") private armor!: IArmor;
  
  private isReady = false;
  
  @postConstruct()
  private initialize() {
    this.weapon.sharpen();
    this.armor.polish();
    this.isReady = true;
    console.log("Ninja is ready for battle!");
  }
  
  fight() {
    if (!this.isReady) {
      throw new Error("Ninja not initialized");
    }
    return this.weapon.attack();
  }
}

@preDestroy

Marks a method to be called before the object is destroyed or the container is disposed.

function preDestroy(target: any, propertyKey: string): void;
@injectable()
class DatabaseService {
  @inject("Connection") private connection!: IConnection;
  
  @preDestroy()
  private cleanup() {
    console.log("Closing database connection...");
    this.connection.close();
  }
}

Programmatic Decoration

decorate

Applies decorators programmatically instead of using decorator syntax.

function decorate(
  decorator: ClassDecorator | ParameterDecorator | PropertyDecorator,
  target: any,
  propertyKey?: string | symbol,
  parameterIndex?: number
): void;
class Ninja {
  constructor(private weapon: IWeapon, private armor: IArmor) {}
}

// Apply decorators programmatically
decorate(injectable(), Ninja);
decorate(inject("Weapon"), Ninja, 0);
decorate(inject("Armor"), Ninja, 1);

// For properties
class Warrior {
  private weapon!: IWeapon;
}

decorate(injectable(), Warrior);
decorate(inject("Weapon"), Warrior.prototype, "weapon");

Decorator Combination Examples

Complex Injection Scenarios

@injectable()
class AdvancedNinja {
  constructor(
    // Primary weapon - required
    @inject("Weapon") @named("primary") 
    private primaryWeapon: IWeapon,
    
    // Secondary weapons - array injection
    @multiInject("Weapon") @tagged("category", "secondary")
    private secondaryWeapons: IWeapon[],
    
    // Optional shield
    @inject("Shield") @optional()
    private shield?: IShield,
    
    // Configuration - unmanaged
    @unmanaged()
    private config: NinjaConfig
  ) {}
  
  @postConstruct()
  private setup() {
    this.primaryWeapon.prepare();
    this.secondaryWeapons.forEach(weapon => weapon.prepare());
  }
  
  @preDestroy()
  private cleanup() {
    this.primaryWeapon.cleanup();
    this.secondaryWeapons.forEach(weapon => weapon.cleanup());
  }
}

Metadata Types

type MetadataName = string | number | symbol;
type MetadataTag = string | number | symbol;

interface ResolvedValueInjectOptions {
  skipBaseClassChecks?: boolean;
}

interface ResolvedValueMetadataInjectOptions extends ResolvedValueInjectOptions {
  key: MetadataName;
}

interface ResolvedValueMetadataInjectTagOptions extends ResolvedValueMetadataInjectOptions {
  value: any;
}

Container Operation Types

type Bind<T> = (serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
type Unbind = (serviceIdentifier: ServiceIdentifier) => void;
type IsBound = (serviceIdentifier: ServiceIdentifier) => boolean;
type Rebind<T> = (serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
type RebindSync<T> = (serviceIdentifier: ServiceIdentifier<T>) => BindInFluentSyntax<T>;
type UnbindSync = (serviceIdentifier: ServiceIdentifier) => void;

Best Practices

  1. Always use @injectable: Required on all classes participating in DI
  2. Prefer constructor injection: More reliable than property injection
  3. Use specific service identifiers: Avoid generic types like "string" or "number"
  4. Combine decorators thoughtfully: Named and tagged decorators work together
  5. Handle optional dependencies: Use @optional() for non-critical dependencies
  6. Leverage lifecycle hooks: Use @postConstruct and @preDestroy for proper resource management

docs

binding.md

conditional.md

container.md

decorators.md

index.md

lifecycle.md

modules.md

tile.json