admin管理员组

文章数量:1178544

With TypeScript now we have static analyze and many OOP features in JavaScript. So it's also time to have better unit tests in client side logic and as well we need IOC container for dependency injections to make code more testable...

So, have someone already experienced it this subject or maybe know libraries for typescript or JavaScript frameworks that can be porting to TypeScript?

With TypeScript now we have static analyze and many OOP features in JavaScript. So it's also time to have better unit tests in client side logic and as well we need IOC container for dependency injections to make code more testable...

So, have someone already experienced it this subject or maybe know libraries for typescript or JavaScript frameworks that can be porting to TypeScript?

Share Improve this question asked Oct 9, 2012 at 8:15 Denis AgarevDenis Agarev 1,5294 gold badges17 silver badges34 bronze badges 3
  • 3 At runtime, there is no TypeScript; There is just Javascript. So their is no static type system at runtime, and for IoC, we'll need a type system at runtime. – Steven Commented Oct 9, 2012 at 9:12
  • Yes I know, but the is no c#|Delphi etc at runtime too, and there is already way to debug TypeScript using SourceMap (stackoverflow.com/questions/12711826/…). – Denis Agarev Commented Oct 9, 2012 at 9:19
  • 2 That's true, but in .NET we can determine which types a constructor needs by using reflection. Such feature is not available for javascript. – Steven Commented Oct 9, 2012 at 9:22
Add a comment  | 

7 Answers 7

Reset to default 21

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

I have created DI library for typescript - huject

https://github.com/asvetliakov/huject

Example:

import {Container, FactoryMethod, ConstructorInject, Inject} from 'huject';
class FirstService {
   public constructor(param: number) {}
}
class SecondService {
}

@ConstructorInject
class MyController {
    @Inject
    public service: FirstService;

    public second: SecondService;
    public constructor(service: SecondService) {
        this.second = service;
    }
    ...
}
container.setAllowUnregisteredResolving(true);
container.register(FirstService, [10]); // Register constructor arguments

// FirstService and SecondService will be resolved for container instance
let controller = container.resolve(MyController);

There is a problem with TypeScript interfaces though, but i have 2 workarounds (use abstract or simple class as interface)

Alternatively you can just use no framework and use class as container with object factories as properties. You can then inherit this class in tests and change factories. This approach is type safe and do not require any decorators, just registration of classes.

class B {
    echo() {
        alert('test');
    }   
}

class A {
    constructor(private b: B) {
        b.echo();
    }
}

class Container {
    A = () => new A(this.B());
    B = singleton(() => new B()); 
}

var c = new Container();
var a = c.A();

// singleton helper:

function memoize<T>(factory: () => T): () => T  {
    var memo: T = null;
    return function () {
        if(!memo) {
            memo = factory();
        }
        return memo;
    };
}

var singleton = memoize;

For now, you can use dependency injection in JavaScript without the IOC part. It is up to you whether you write a "manual" resolver, or factories, or whatever DI pattern you prefer.

When the ECMAScript 6 standard is adopted, it may make the concept of IOC possible in JavaScript.

We've been using a simple dependency injection container which uses AMD define/require - like syntax. The original implementation is in TypeScript, although the blog post below presents it in plain old JavaScript.

http://blog.coolmuse.com/2012/11/11/a-simple-javascript-dependency-injection-container/

It's pretty straightforward to define dependency relationships without requiring a bunch of configuration, and supports circular dependency resolution similar to requirejs.

Here's a simple example:

// create the kernel
var kernel = new ServiceKernel();

// define service1
kernel.define("service1", function() {

    // service1 has a function named foo
    return {
        foo: function () { return "foo"; }
    }

});

// define service2, which depends on service1
kernel.define("service2", ["service1"], function(service1) {

    // service2 has a function named foobar
    return {
        foobar : function() { return service1.foo() + "bar"; }
    }

});

// get service2 instance 
var service2 = kernel.require("service2");
service2.foobar();  // returns "foobar"

// get both service1 and service2 instances
kernel.require(["service1", "service2"], function(service1, service2) {

    alert(service1.foo() + service2.foobar()); // displays foofoobar

});

I used to use TSyringe for small to medium sized project. It works great, but my conclusion is that I don't need dependency injection in JavaScript / TypeScript.

Since JavaScript is a dynamic language, all implementations can be changed at runtime. If you just want to inject stub object for unit testing, use SinonJS or similar tools.

Why:

  • It brings unnecessary complexity and long boilerplate to your code.
  • Everything should be written as Class, so TypeScript cannot infer proper types for some methods, meaning you should manually type arguments for them.
  • Most of Dependency Injection frameworks rely on TypeScript decorators, which is slow for compilation. And the feature is still experimental (This could be changed as time passes)

checkout https://github.com/typestack/typedi

something like this is possible:

import "reflect-metadata";
import {Service, Container} from "typedi";

@Service()
class SomeClass {

    someMethod() {
    }

}

let someClass = Container.get(SomeClass);
someClass.someMethod();

本文标签: javascriptIOC for TypeScriptStack Overflow