Announcing InversifyJS 2.0.0 Release Candidate ?
Learn what’s new in InversifyJS 2.0.0 and what can you expect to see in the future of this powerful lightweight IoC container for JavaScript and Node.js apps powered by TypeScript
As many of the readers of this blog already know, over the past year and a half we have been working on InversifyJS.
InversifyJS is a powerful lightweight IoC container for JavaScript and Node.js written in TypeScript.
Last march we announced that we were working on InversifyJS 2.0. Today we are happy to announce that InversifyJS 2.0 release candidate is available on npm.
In this post we are going to learn about the following:
- Setting up InversifyJS 2.0 with TypeScript
- The InversifyJS 2.0 API
- The InversifyJS 2.0 features
- The InversifyJS ecosystem
- The InversifyJS community
Let’s get started!
Setting up InversifyJS 2.0 with TypeScript
$ npm install inversify@2.0.0-beta.5 reflect-metadata --save
$ npm install inversify-dts --save-dev
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
Please refer to the InversifyJS installation guide for more details.
The InversifyJS 2.0 API
The InversifyJS API has changed a lot over the last couple of months, so let’s take a look to the final syntax.
The following example is not the most basic example but it is a good example to showcase the power of InversifyJS.
The first thing we need to do is to import reflect-metadata
, the injectable
, named
and inject
decorators and the Kernel
class from ImversifyJS and the required TypeScript type definitions:
/// <reference path="./node_modules/inversify-dts/inversify/inversify.d.ts" />
/// <reference path="./node_modules/reflect-metadata/reflect-metadata.d.ts" />
import { Kernel, injectable, inject, named } from "inversify";
import "reflect-metadata";
Then we can declare some interfaces:
interface Warrior {
fight(): string;
}
interface Weapon{
use(): string;
}
interface Battlefield{
warrior1: Warrior;
warrior2: Warrior;
}
We are also going to declare some Symbols
and tags. These are constants that are used as IDs and metadata. Because the constants are strings literals it is better to declare them just once in the whole application and that is what we are doing here:
let TYPES = {
Warrior: Symbol("Warrior"),
Weapon: Symbol("Weapon"),
Battlefield: Symbol("Battlefield")
};
let TAGS = {
ninja: "ninja",
samurai: "samurai"
};
We can then implement the interfaces. In this case we are going to declare two implementations of the Weapon
interface:
@injectable()
class Katana implements Weapon {
public use() {
return "Used Katana!";
}
}
@injectable()
class Shuriken implements Weapon {
public use() {
return "Used Shuriken!";
}
}
One implementation of the Warrior
interface:
@injectable()
class Warrior implements Warrior {
private _weapon: Weapon;
public constructor(
@inject(TYPES.Weapon) weapon: Weapon
) {
this._weapon = weapon;
}
public fight() { return this._weapon.use(); };
}
One implementation of the Battlefield
interface:
@injectable()
class Battlefield implements Battlefield {
public warrior1: Warrior;
public warrior2: Warrior;
public constructor(
@inject(TYPES.Warrior) @named(TAGS.ninja) warrior1: Warrior,
@inject(TYPES.Warrior) @named(TAGS.samurai) warrior2: Warrior
) {
this.warrior1 = warrior1;
this.warrior2 = warrior2;
}
}
We need to annotate all these classes with the injectable
decorator and if the class has dependencies we also need to use the inject
decorator to declare the type of the dependency.
Now that our entities are ready, we can create an instance of the InversifyJS kernel.
We then need to declare some bindings. A binding is a map between an interface and an implementation of that type. A binding can also have constraints. Constraints allow us to limit the binding to a context. In this case we are going to declare the following constraints:
- When an instance of
Weapon
is needed, return aKatana
if its parent (Warrior
) is named “samurai”. - When an instance of
Weapon
is needed, return aShuriken
if its parent (Warrior
) is named “ninja”.
var kernel = new Kernel();
kernel.bind<Weapon>(TYPES.Weapon)
.to(Katana)
.whenParentNamed(TAGS.samurai);
kernel.bind<Weapon>(TYPES.Weapon)
.to(Shuriken)
.whenParentNamed(TAGS.ninja);
kernel.bind<Warrior>(TYPES.Warrior)
.to(Warrior);
kernel.bind<Battlefield>(TYPES.Battlefield)
.to(Battlefield);
At this point we can request an instance of Battlefield
and the InversifyJS Kernel will be able to resolve
let battlefield = kernel.get<Battlefield>(TYPES.Battlefield);
battlefield.warrior1.fight(); // > "Used Shuriken!"
battlefield.warrior2.fight(); // > "Used Katana!"
The InversifyJS 2.0 features
InversifyJS 2.0 is really powerful and its list of features is very extensive:
- NodeJS and Browser support.
- ES6 and ES5 support.
- TypeScript and JavaScript support.
- Kernel modules
- Kernel middleware
- Kernel snapshot
- Kernel middleware
- Kernel children (Hierarchical DI systems)
- Kernel.getAll(), Kernel.getNamed() & Kernel.getTagged()
- Use classes, string literals or Symbols as dependency identifiers
- Singleton & Transient scope
- Property Injection
- Injection of constant and dynamic values
- Injection of class constructors
- Injection of factories
- Auto factory
- Injection of providers (async factory)
- Activation handlers (used to inject proxies)
- Multi injections
- Tagged bindings
- Custom tag decorators
- Named bindings
- Contextual bindings
- Friendly exceptions (e.g. Circular dependencies)
You can visit GitHub or www.inversify.io to learn more about these features.
The InversifyJS ecosystem
I believe that InversifyJS is great but we think that being great is not enough. I want to provide the community with an awesome library and awesome ecosystem of extensions and development tools. Let’s take a look to some of them.
The inversify-binding-decorators project
There is an alternative binding syntax that uses decorators. Instead of writing:
import { injectable, Kernel } from "inversify";
import "reflect-metadata";
@injectable()
class Katana implements Weapon {
public hit() {
return "cut!";
}
}
@injectable()
class Shuriken implements ThrowableWeapon {
public throw() {
return "hit!";
}
}
var kernel = new Kernel();
kernel.bind<Weapon>("Weapon").to(Katana);
kernel.bind<ThrowableWeapon>("ThrowableWeapon").to(Shuriken);
We can write the following:
import { injectable, Kernel } from "inversify";
import makeProvideDecorator from "inversify-binding-decorators";
import "reflect-metadata";
var kernel = new Kernel();
let provide = makeProvideDecorator(kernel);
@provide("Weapon")
class Katana implements Weapon {
public hit() {
return "cut!";
}
}
@provide("ThrowableWeapon")
class Shuriken implements ThrowableWeapon {
public throw() {
return "hit!";
}
}
The inversify-binding-decorators project also allows us to declare contextual constraints using decorators:
@provide("Weapon").whenTargetTagged("throwable", false).done();
class Katana implements Weapon {
public hit() {
return "cut!";
}
}
You can learn more about this project on GitHub.
The inversify-logger-middleware project
This project provides you with a configurable console logging middleware:
This middle ware can help you to identify issues and to have a better understanding of the object composition.
You can learn more about this project on GitHub.
The inversify-devtools project [Coming Soon]
This project provides InversifyJS developers with a browser extension that helps you to work with InversifyJS in front-end applications.
You can learn more about this project on GitHub.
The inversify-express-utils project
This projects provides some utilities for the development of Node.js application with Express.
You can learn more at GiiHub.
Other projects
The list of projects does not end here! Check out the InversifyJS ecosystem page on GitHub to learn about other projects.
The InversifyJS community
The InversifyJS community is still relatively small but it has been growing steadily over the last few months. There are other libraries and frameworks depending on it already.
Thanks a lot to all the contributors, all the developers out there using InversifyJS and all those that help us to spread the word by sharing content about InversifyJS online. Without your feedback and support this project would not be possible.
The future of InversifyJS
Now that the core library is stable we will focus on improving documentations and examples. We will also work in development tools so you guys can enjoy a state of the art development experience.
There are two major features that we are planning to try to implement and I believe are worth mention here:
The best way to be updated is to check the issues page on GitHub.
Summary
I’ve been working on InversifyJS for over a year and it has been an amazing experience.
I believe that InversifyJS a lot of potential and we are just getting started. I hope that the community will continue to grow and I can’t wait to see what amazing stuff the people will build with it.
Please try InversifyJS and let us know what do you think. We want to define a road map based on the real needs of real users and the only way we can do that is if you let us know what you need and what you don’t like.
Remember that you can visit http://inversify.io/ or the GitHub repo to learn more about InversifyJS.
Please feel free to let us know your thoughts about this article with us via @OweR_ReLoaDeD, @WolkSoftwareLtd and @InversifyJS.
Don’t forget to subscribe if you don’t want to miss it out future articles!