A powerful and lightweight inversion of control container for JavaScript and Node.js apps powered by TypeScript.
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
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.
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;
}
}Specifies which service should be injected into a constructor parameter or class property.
function inject(serviceIdentifier: ServiceIdentifier): ParameterDecorator | PropertyDecorator;@injectable()
class Ninja {
constructor(
@inject("Weapon") private weapon: IWeapon,
@inject("Armor") private armor: IArmor
) {}
}@injectable()
class Ninja {
@inject("Weapon")
private weapon!: IWeapon;
@inject("Armor")
public armor!: IArmor;
}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[];
}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");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");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;
}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);
}
}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);
}
}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);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();
}
}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();
}
}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");@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());
}
}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;
}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;