Nodeで書く時TypeScriptでDIしたい

  • NodeでもTypeScript で書きたい
  • DI欲しくなる
    • シングルトンなサービスを使いたい
    • new したくない
    • テストしやすい

リポジトリ: https://github.com/cyokodog/inversifyjs_study

InversifyJS

準備

npm install inversify reflect-metadata --save

tsconfig.json

{
    "compilerOptions": {
        "target": "es5",
        "lib": ["es6"],
        "types": ["reflect-metadata"],
        "module": "commonjs",
        "moduleResolution": "node",
        "experimentalDecorators": true,
        "emitDecoratorMetadata": true
    }
}

サンプル

service.ts

import { injectable } from "inversify";

@injectable()
export class Katana {
  sound = "ブンッ!";

  swing() {
    return this.sound;
  }
}

@injectable()
export class Samurai {
  constructor(private katana: Katana) {}

  fight() {
    return this.katana.swing();
  }

  pwoerUp() {
    this.katana.sound = "ブウウウウンッ!!";
  }
}

inversify.config.ts

import { Container } from "inversify";
import { Samurai, Katana } from "./services";

const rootContainer = new Container();
rootContainer.bind(Katana).toSelf();
rootContainer.bind(Samurai).toSelf();

export const container = rootContainer.createChild();

実行!

import "reflect-metadata";
import { container } from "./inversify.config";
import { Samurai } from "./services";

const samurai = container.get(Samurai);

console.log(samurai.fight()); // ブンッ!
samurai.pwoerUp();
console.log(samurai.fight()); // ブウウウウンッ!!

シングルトンになってるかな?

const samuraiA = container.get(Samurai);
const samuraiB = container.get(Samurai);

samuraiA.pwoerUp();
console.log(samuraiA.fight()); // ブウウウウンッ!!
console.log(samuraiB.fight()); // ブンッ!

なってない....!

inSingletonScope()

シングルトンにするには bind() する際に、 inSingletonScope() する必要がある

rootContainer.bind(Samurai).toSelf().inSingletonScope();
const samuraiA = container.get(Samurai);
const samuraiB = container.get(Samurai);

samuraiA.pwoerUp();
console.log(samuraiA.fight()); // ブウウウウンッ!!
console.log(samuraiB.fight()); // ブウウウウンッ!!

モックで差し替えてみる

@injectable()
export class KatanaMock implements Katana {
  sound = "スカッ!";

  swing() {
    return this.sound + "(mock)";
  }
}
container.bind(Katana).to(KatanaMock);

const samuraiA = container.get(Samurai);
const samuraiB = container.get(Samurai);

console.log(samuraiA.fight()); // スカッ!(mock)
samuraiA.pwoerUp();
console.log(samuraiB.fight()); // ブウウウウンッ!!(mock)

良さそう、使ってみる!