Migrating from v6
InversifyJS v7 introduces several breaking changes. This guide will help you migrate your existing InversifyJS v6 code to v7.
Overview of Changes
The following table summarizes the key changes from v6 to v7:
v6 | v7 | Comment |
---|---|---|
autoBindInjectable option | autobind option | Renamed container constructor option |
container.resolve(X) | container.get(X, { autobind: true }) | resolve replaced by get with autobind option |
Custom metadata and middlewares | No direct replacement | Removed to simplify the library and avoid exposing internal data structures |
container.isBoundNamed | container.isBound(X, { name: ... }) | Merged all isBound variants with an options parameter |
container.isBoundTagged | container.isBound(X, { tag: ... }) | Merged all isBound variants with an options parameter |
container.getNamed | container.get(X, { name: ... }) | Merged all get variants with an options parameter |
container.getTagged | container.get(X, { tag: ... }) | Merged all get variants with an options parameter |
container.tryGet | container.get(X, { optional: true }) | Added optional flag for optional get |
container.tryGetNamed | container.get(X, { name: ..., optional: true }) | Combined named and optional parameters |
container.tryGetTagged | container.get(X, { tag: ..., optional: true }) | Combined tagged and optional parameters |
container.load | await container.load | Now returns a Promise |
container.load | container.loadSync | Synchronous alternative to async load |
container.unload | await container.unload | Now returns a Promise |
container.unload | container.unloadSync | Synchronous alternative to async unload |
container.rebind | await container.rebind or container.rebindSync | Now async, with a sync alternative |
container.unbind | await container.unbind or container.unbindSync | Now async, with a sync alternative |
container.createChild() | new Container({ parent: container }) | Child containers created via constructor |
interfaces.Context | ResolutionContext | Updated context parameter type in the binding API |
interfaces.Request | BindingConstraints | Updated binding constraint parameter type |
interfaces.Provider<T> | Provider<T> | Directly exported, no longer under interfaces namespace |
interfaces.Factory<T> | Factory<T> | Directly exported, no longer under interfaces namespace |
interfaces.Newable<T> | Newable<T> | Directly exported, no longer under interfaces namespace |
Implicit injection inheritance | @injectFromBase decorator | Explicit inheritance with a decorator |
import { interfaces } from "inversify"; | import { ... } from "inversify7"; | Removed interfaces namespace, use direct imports |
Container API
Autobinding
In v6, autobinding was enabled by passing the autoBindInjectable
option to the container constructor. In v7, this option has been renamed to autobind
and can be passed either as part of the Container
constructor options or Container.get
options.
In v6, container.resolve
automatically bound the resolved service to the container. In v7, this behavior has been removed. To enable it, pass the autobind
option.
export class Katana {
public readonly damage: number = 10;
}
@injectable()
export class Samurai {
public readonly katana: Katana;
constructor(katana: Katana) {
this.katana = katana;
}
}
const container: Container = new Container();
const samurai: Samurai = container.get(Samurai, { autobind: true });
Custom Metadata and Middlewares
This feature has been removed in v7 with no direct replacement. It was not widely used and contributed to the library's complexity. A better API may be introduced in the future.
isBound
-like Methods
Methods like Container.isBoundNamed
and Container.isBoundTagged
have been replaced by Container.isBound
with an optional isBoundOptions
parameter to handle named and tagged bindings.
Refer to the API documentation for isBound and isCurrentBound for more details.
get
-like Methods
The Container.getNamed
, Container.getTagged
, Container.tryGet
, Container.tryGetNamed
, and Container.tryGetTagged
methods have been replaced by Container.get
with an OptionalGetOptions
parameter.
Similarly, Container.getAll
, Container.getAllAsync
, and Container.getAsync
now accept a GetOptions
object to specify names or tags.
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana).whenNamed('Katana');
const katana: Weapon = container.get<Weapon>('Weapon', { name: 'Katana' });
Additionally, Container.getAll
and Container.getAllAsync
now enforce binding constraints. For example:
const container: Container = new Container();
container.bind<Weapon>('Weapon').to(Katana);
container.bind<Weapon>('Weapon').to(Shuriken).whenNamed('ranged');
const weapons: Weapon[] = container.getAll<Weapon>('Weapon');
In v6, container.getAll
returned all bindings matching the service identifier. In v7, it only returns bindings that match both the service identifier and binding constraints.
load
and unload
Methods
These methods are now asynchronous and return a Promise. Synchronous alternatives, loadSync
and unloadSync
, are also available.
const warriorsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Ninja>('Ninja').to(Ninja);
},
);
const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana).whenNamed('Melee');
options.bind<Shuriken>('Weapon').to(Shuriken).whenNamed('Ranged');
},
);
await container.load(warriorsModule, weaponsModule);
const ninja: Ninja = container.get('Ninja');
rebind
Method
The Container.rebind
method is now asynchronous and returns a Promise. A synchronous alternative, Container.rebindSync
, is also available.
unbind
and unbindAll
Methods
The Container.unbind
method is now asynchronous and returns a Promise. A synchronous alternative, Container.unbindSync
, is also available.
Parent and Child Containers
In v6, child containers were created using the createChild
method. In v7, this method has been removed. Instead, pass the parent container to the constructor of the child container.
class Katana {}
const parentContainer: Container = new Container();
parentContainer.bind(weaponIdentifier).to(Katana);
const childContainer: Container = new Container({ parent: parentContainer });
const katana: Katana = childContainer.get(weaponIdentifier);
ContainerModule API
Container module load options are now passed as an object.
const warriorsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Ninja>('Ninja').to(Ninja);
},
);
const weaponsModule: ContainerModule = new ContainerModule(
(options: ContainerModuleLoadOptions) => {
options.bind<Katana>('Weapon').to(Katana).whenNamed('Melee');
options.bind<Shuriken>('Weapon').to(Shuriken).whenNamed('Ranged');
},
);
await container.load(warriorsModule, weaponsModule);
const ninja: Ninja = container.get('Ninja');
BindingFluentSyntax API
ResolutionContext
Instead of interfaces.Context
The Context
class has been replaced by ResolutionContext
to simplify the API and hide internal data structures.
@injectable()
class Katana {
public use(): string {
return 'hit!';
}
}
container
.bind<Katana>('Katana')
.to(Katana)
.onActivation((_context: ResolutionContext, katana: Katana) => {
const handler: ProxyHandler<() => string> = {
apply: function (
target: () => string,
thisArgument: unknown,
argumentsList: [],
) {
console.log(`Starting: ${new Date().getTime().toString()}`);
const result: string = target.apply(thisArgument, argumentsList);
console.log(`Finished: ${new Date().getTime().toString()}`);
return result;
},
};
katana.use = new Proxy(katana.use.bind(katana), handler);
return katana;
});
BindingConstraints
Instead of interfaces.Request
The Request
object has been replaced by BindingConstraints
to simplify the API and hide internal data structures.
const ninjaId: symbol = Symbol.for('Ninja');
const weaponId: symbol = Symbol.for('Weapon');
@injectable()
class Ninja {
constructor(
@inject(weaponId)
@named('shuriken')
public readonly weapon: Weapon,
) {}
}
container.bind<Ninja>(ninjaId).to(Ninja);
const whenTargetNamedConstraint: (
name: string,
) => (bindingconstraints: BindingConstraints) => boolean =
(name: string) =>
(bindingconstraints: BindingConstraints): boolean =>
bindingconstraints.name === name;
container
.bind<Weapon>(weaponId)
.to(Katana)
.when(whenTargetNamedConstraint('katana'));
container
.bind<Weapon>(weaponId)
.to(Shuriken)
.when(whenTargetNamedConstraint('shuriken'));
const ninja: Ninja = container.get(ninjaId);
// Returns 5
const ninjaDamage: number = ninja.weapon.damage;
Decorators API
Inheritance
Injection inheritance is now explicit using the @injectFromBase
decorator. This provides more control and avoids edge cases related to constructor argument mismatches.
For more details, refer to the Inheritance documentation.