web-dev-qa-db-ja.com

TypeScriptでの依存性注入

TypeScriptでTDDを実行する可能性を検討しています。 TypeScriptでテストを作成する場合、インポートステートメントでテスト中のクラスのモックを返すことは可能ですか?または、純粋なJavaScriptでテストを記述し、AMDを自分で注入することを処理する唯一の実行可能なアプローチは何ですか?

28
Sop Killen

TypeScriptの依存性注入に infuse.js を使用します。

D.tsを参照する

/// <reference path="definition/infusejs/infusejs.d.ts"/>

起動時にインジェクターを初期化する

this.injector = new infuse.Injector();  

マップの依存関係

this.injector.mapClass( 'TodoController', TodoController );
this.injector.mapClass( 'TodoView', TodoView );
this.injector.mapClass( 'TodoModel', TodoModel, true );  // 'true' Map as singleton

依存関係を注入する

export class TodoController
{
    static inject = ['TodoView', 'TodoModel'];

    constructor( todoView:TodoView, todoModel:TodoModel )
    {

    }
 }

タイプベースではなく文字列ベースです(リフレクションはTypeScriptではまだ可能ではないため)。それにもかかわらず、それは私のアプリケーションで非常にうまく機能します。

20
Philip Bulley

私は、コンテキストバインディングなどの高度な依存関係注入機能を備えたInversifyJSと呼ばれるIoCコンテナーを開発しました。

それを使用するには、3つの基本的な手順に従う必要があります。

1.注釈を追加する

アノテーションAPIは、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.バインディングを宣言する

バインディングAPIは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.依存関係を解決する

解決APIはNinjectに基づいています:

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

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

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

最新リリース(2.0.0)は、多くのユースケースをサポートしています。

  • カーネルモジュール
  • カーネルミドルウェア
  • 依存識別子としてクラス、文字列リテラル、またはシンボルを使用する
  • 定数値の注入
  • クラスコンストラクターの挿入
  • 工場への注入
  • 自動車工場
  • プロバイダーの注入(非同期ファクトリー)
  • アクティブ化ハンドラー(プロキシーの挿入に使用)
  • マルチ注射
  • タグ付きバインディング
  • カスタムタグデコレータ
  • 名前付きバインディング
  • コンテキストバインディング
  • フレンドリーな例外(循環依存など)

詳細は https://github.com/inversify/InversifyJS で確認できます。

18
Remo H. Jansen

これを試してください Dependency Injector(Typejector)

GitHub Typejector

新しいTypeScript 1.5では、アノテーション方法を使用できます

例えば

    @injection
    class SingletonClass {
        public cat: string = "Kitty";
        public dog: string = "Hot";

        public say() {
            alert(`${this.cat}-Cat and ${this.dog}-Dog`);
        }
    }
    @injection
    class SimpleClass {
        public say(something: string) {
            alert(`You said ${something}?`);
        }
    }

    @resolve
    class NeedInjectionsClass {
        @inject(SingletonClass)
        public helper: SingletonClass;
        @inject(SimpleClass)
        public simpleHelper: SimpleClass;

        constructor() {
            this.helper.say();
            this.simpleHelper.say("wow");
        }
    }
    class ChildClass extends NeedInjectionsClass {

    }

    var needInjection = new ChildClass();

質問の場合:次の例のように、一部のプロパティは疑似インターフェイス(または抽象クラス)を実現する必要があります。

    class InterfaceClass {
        public cat: string;
        public dog: string;

        public say() {

        }
    }

    @injection(true, InterfaceClass)
    class SingletonClass extends InterfaceClass {
        public cat: string = "Kitty";
        public dog: string = "Hot";

        public say() {
            alert(`${this.cat}-Cat and ${this.dog}-Dog`);
        }
    }

    @injection(true, InterfaceClass)
    class MockInterfaceClass extends InterfaceClass {
        public cat: string = "Kitty";
        public dog: string = "Hot";

        public say() {
            alert(`Mock-${this.cat}-Cat and Mock-${this.dog}-Dog`);
        }
    }

    @injection
    class SimpleClass {
        public say(something: string) {
            alert(`You said ${something}?`);
        }
    }

    @resolve
    class NeedInjectionsClass {
        @inject(InterfaceClass)
        public helper: InterfaceClass;
        @inject(SimpleClass)
        public simpleHelper: SimpleClass;

        constructor() {
            this.helper.say();
            this.simpleHelper.say("wow");
        }
    }

    class ChildClass extends NeedInjectionsClass {

    }

    var needInjection = new ChildClass();

注:モックインジェクションは、インターフェイスのクラスクリエーターを再定義するため、ソースコードの後に​​定義する必要があります

6
Oleh Dokuka

Angular2を使用する人のために、私はFluency Injection https://www.npmjs.com/package/fluency-injection を開発しました。ドキュメントは非常に完全であり、Angular2のDIの動作を模倣しています。

フィードバックは大歓迎です、それがあなたの役に立てば幸いです:)

2
Connor Wyatt

これを試してみることができます: https://www.npmjs.com/package/easy-injectionjs 。これは、汎用的な依存関係注入パッケージです。

@EasySingletonは、アプリケーション全体で依存関係の単一インスタンスを作成します。ある種のサービスに最適です。

@EasyPrototypeは、依存関係のインスタンスを必要な数だけ作成します。変更可能な依存関係に最適です。

@EasyFactoryは主に継承に使用されます。

このパッケージを使用して何でもできます:簡単な使用法(readmeから):

import { Easy, EasyFactory, EasyPrototype, EasySingleton } from 'easy-injectionjs';

@EasyFactory()
abstract class Person {
  abstract getName();
  abstract setName(v: string);
}

// @EasyObservable()
@EasySingleton()
class Somebody extends Person{
  // @Easy()
  constructor (private name: string) {
    super()
    this.name = 'Sal';
  }

  public getName() {
    return this.name;
  }
  public setName(v: string) {
    this.name = v;
  }
}

@EasyPrototype()
class Nobody extends Person{
  @Easy()
  somebody: Person;
  constructor () {
    super()
  }

  public getName() {
    return this.somebody.getName();
  }

  public setName(v: string) {
    this.somebody.setName(v);
  }
}

@EasyPrototype()
class Data {
  @Easy()
  somebody: Person;
  name: string;

  change(v: string) {
    this.somebody.setName(v);
  }

  getName(): string {
    return this.somebody.getName();
  }
}

let n = new Nobody();
console.log(n.getName()) // Prints Sal
n.setName('awesome');
console.log(n.getName())  // Prints awesome
let d = new Data()
console.log(d.getName())  // Prints awesome
d.change('Gelba')
console.log(n.getName())  // Prints Gelba
d.change('kaa')
console.log(n.getName())  // Prints Kaa

ノードモジュールを注入したい場合でも、これを行うことができます:

import * as IExpress from 'express';
import { Easy, EasySingleton } from 'easy-injectionjs';

@EasySingleton()
class Express extends IExpress {} 

@EasySingleton()
export class App {
  @Easy()
  private _express: Express;
}

let app = new App();
console.log(app)

もちろん、高速サーバーの使用はコンソールのロギング用ではありません。これはテスト用です:D。

それが役に立てば幸い:D

0
Saleh Shehata

TypeScriptは、requirejsなどのAMDローダーで適切に動作します。 TypeScriptが適切に構成されていれば、完全にAMD準拠のJavaScriptが出力されます。

テスト状況では、テスト可能なモジュールを挿入するようにrequirejsを構成できます。

0
Alex Dresko

あなたは解決策を使うことができます:
JavaScript/TypeScriptの軽量の依存性注入コンテナ

import {autoInjectable, container} from "tsyringe";

class MyService {
  move(){
    console.log('myService move 123', );
  }
}

class MyServiceMock {
  move(){
    console.log('mock myService move 777', );
  }
}

@autoInjectable()
export class ClassA {
  constructor(public service?: MyService) {
  }
  move(){
    this.service?.move();
  }
}

container.register(MyService, {
  useClass: MyServiceMock
});

new ClassA().move();

出力:

myService move 777のモック

0
ZoomAll