1

Is there any typescript framework which allow user, to use well-known from java-spring autowiring? Example

class A {
   public print() : void;
}

class B {
   constructor(a : A)
}
var obj = CONTAINER.get("B");

And container is part of API

0

2 Answers 2

5

I have developed an IoC container called InversifyJS with advanced dependency injection features like contextual bindings.

You need to follow 3 basic steps to use it:

1. Add annotations

The annotation API is based on Angular 2.0:

import { injectable, inject } from "inversify";

@injectable()
class Katana implements IKatana {
    public hit() {
        return "cut!";
    }
}

@injectable()
class Shuriken implements IShuriken {
    public throw() {
        return "hit!";
    }
}

@injectable()
class Ninja implements INinja {

    private _katana: IKatana;
    private _shuriken: IShuriken;

    public constructor(
        @inject("IKatana") katana: IKatana,
        @inject("IShuriken") shuriken: IShuriken
    ) {
        this._katana = katana;
        this._shuriken = shuriken;
    }

    public fight() { return this._katana.hit(); };
    public sneak() { return this._shuriken.throw(); };

}

2. Declare bindings

The binding API is based on Ninject:

import { Kernel } from "inversify";

import { Ninja } from "./entities/ninja";
import { Katana } from "./entities/katana";
import { Shuriken} from "./entities/shuriken";

var kernel = new Kernel();
kernel.bind<INinja>("INinja").to(Ninja);
kernel.bind<IKatana>("IKatana").to(Katana);
kernel.bind<IShuriken>("IShuriken").to(Shuriken);

export default kernel;

3. Resolve dependencies

The resolution API is based on Ninject:

import kernel = from "./inversify.config";

var ninja = kernel.get<INinja>("INinja");

expect(ninja.fight()).eql("cut!"); // true
expect(ninja.sneak()).eql("hit!"); // true

The latest release (2.0.0) supports many use cases:

  • Kernel modules
  • Kernel middleware
  • Use classes, string literals or Symbols as dependency identifiers
  • Injection of constant 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 learn more about it at https://github.com/inversify/InversifyJS

Update May 2016

Autowiring in compiled programming languages like C# looks as the following:

kernel.Scan(scanner =>
{
    // look for types in this assembly
    scanner.FromCallingAssembly();

    // make ISomeType bind to SomeType by default (remove the 'I'!)
    scanner.BindWith<DefaultBindingGenerator>();
});

or

kernel.Bind(
    x => x.FromThisAssembly()
          .SelectAllClasses()
          .BindAllInterfaces());

In TypeScript and JavaScript there are no assemblies. We could create a task that search across your modules for classes with the @injectable() decorator but that would be really inefficient.

We are working on an extension for InversifyJS that allows something that we think is like a sort of pseudo autowiring.

Instead of:

@injectable()
class Katana implements IKatana {
    public hit() {
        return "cut!";
    }
}

kernel.bind<IKatana>("IKatana").to(Katana);

You can write:

@provide(Katana)
class Katana implements IKatana {
    public hit() {
        return "cut!";
    }
}

The @provide() decorator generates the binding under the hood:

kernel.bind<Katana>(Katana).to(Katana);

The extension also includes a helper called autoProvide that applies the @provide decorator to all the entities for you.

import * as entites from "../entities";

let kernel = new Kernel();
autoProvide(kernel, entites);
let warrior = kernel.get<Warrior>(entites.Warrior);

The entities module must provide direct access to all your entities:

export { default as Warrior } from "./warrior";
export { default as Katana } from "./katana";

And the entities don't need the @injectable or @provide decorators:

class Katana {
    public use() {
        return "Using Katana...";
    }
}

export default Katana;

However, the @inject decorator is needed:

import Katana from "./katana";
import { inject } from "inversify";

class Warrior {
    private _weapon: Katana;
    public constructor(
        // we need to declare binding because auto-provide uses
        // @injectbale decorator at runtime not compilation time
        // in the future maybe this limitation will desapear
        // thanks to design-time decorators or some other TS feature
        @inject(Katana) weapon: Katana
    ) {
        this._weapon = weapon;
    }
    public fight() {
        return this._weapon.use();
    }
}

export default Warrior;

As I said, this is not autowiring because without assemblies is impossible but it is close enough.

You can learn more about inversify-binding-decorators here.

Sign up to request clarification or add additional context in comments.

2 Comments

Recently Kernel has been renamed to Container.
0

You can also checkout Angular2 DI

https://github.com/angular/di.js

there is also few examples: https://github.com/angular/di.js/tree/master/example/kitchen-di

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.