DEV Community

Cover image for Dependency injection in TypeScript applications powered by InversifyJS

Dependency injection in TypeScript applications powered by InversifyJS

Remo H. Jansen on July 06, 2017

About InversifyJS is a lightweight inversion of control (IoC) container for TypeScript and JavaScript apps. InversifyJS uses annotations...
Collapse
 
theodesp profile image
Theofanis Despoudis • Edited

Great Work Remo,
What are the advantages you think of this library and for example just using a dictionary or a Map of singleton objects that you can pass on to constructors? Something like a Registry Pattern.
Its just I find the decorators in classes to be associated with mixins and I don't find that very appealing

remonsinnema.com/2009/03/01/the-re...

Collapse
 
remojansen profile image
Remo H. Jansen • Edited

Thanks! Good question, InversifyJS allows you to have more control over the life-cycle of dependencies and how the dependency graph is composed than a dictionary of singletons. InversifyJS gives you more power thanks to 3 main features:

Scopes

At the moment the core library allows:

  • Transient scope (one instance each time you call container.get())
  • Singleton scope (same instance for every call to container.get())

I'm working on custom scopes which will allow singletons within a context as opposed to application-level singletons. This will enable things like "Request scope" which can be used to declare singletons at HTTP-request-level.

Contextual constraints

You can inject dependencies based on execution time constraints. InversifyJs supports many types of constraints:

interface BindingWhenSyntax<T> {
    when(constraint: (request: interfaces.Request) => boolean): interfaces.BindingOnSyntax<T>;
    whenTargetNamed(name: string): interfaces.BindingOnSyntax<T>;
    whenTargetTagged(tag: string, value: any): interfaces.BindingOnSyntax<T>;
    whenInjectedInto(parent: (Function|string)): interfaces.BindingOnSyntax<T>;
    whenParentNamed(name: string): interfaces.BindingOnSyntax<T>;
    whenParentTagged(tag: string, value: any): interfaces.BindingOnSyntax<T>;
    whenAnyAncestorIs(ancestor: (Function|string)): interfaces.BindingOnSyntax<T>;
    whenNoAncestorIs(ancestor: (Function|string)): interfaces.BindingOnSyntax<T>;
    whenAnyAncestorNamed(name: string): interfaces.BindingOnSyntax<T>;
    whenAnyAncestorTagged(tag: string, value: any): interfaces.BindingOnSyntax<T>;
    whenNoAncestorNamed(name: string): interfaces.BindingOnSyntax<T>;
    whenNoAncestorTagged(tag: string, value: any): interfaces.BindingOnSyntax<T>;
    whenAnyAncestorMatches(constraint: (request: interfaces.Request) => boolean): interfaces.BindingOnSyntax<T>;
    whenNoAncestorMatches(constraint: (request: interfaces.Request) => boolean): interfaces.BindingOnSyntax<T>;
}
Enter fullscreen mode Exit fullscreen mode

You can do something like:

container.bind<Weapon>("Weapon").to(Katana).whenInjectedInto(Samurai);
container.bind<Weapon>("Weapon").to(Shuriken).whenInjectedInto(Ninja);
Enter fullscreen mode Exit fullscreen mode

Interception

InversifyJS allows you to implement interception thanks to the onActivation handlers and the @postConstruct decorator. You can learn more about IoC and interception here.

Collapse
 
theodesp profile image
Theofanis Despoudis

Great. I suppose this fine level of control will suit more to backend applications or really complex web apps with lots of services when you need something more customizable.

Thread Thread
 
remojansen profile image
Remo H. Jansen

Yes, InversifyJS becomes more valuable as your application grows.

Collapse
 
thohoh profile image
Alex Kozlov • Edited

Thank you for the article. Inversify brough good things in Javascript.

There's also a library called "container-ioc" and is also for Javascript/Typescript & Node.js apps.
It has almost 1 to 1 Angular 4 API and makes use of providers for registration.

I found it more flexible and easier to use in most of my projects.
npmjs.com/package/container-ioc