TSyringeを動かしてみたのでそのメモ
注意 動かしてみた結果と、推測を書いているので誤っている可能性もあります😅
TSyringeとは
Microsoftが開発している Javascript/TypeScript用の軽量DIコンテナです。
コンストラクタインジェクションのみサポートしています。
準備
今回はバージョン4.1.0 を使いました。
動作確認はブラウザ上でコンソールに出力して確認しました。
rem typescript 使うので
yarn add typescript -D
rem tsyringeの準備
yarn add tsyringe reflect-metadata
rem webpackを使う
yarn add webpack webpack-cli webpack-dev-server ts-loader -D
rem 動作確認はブラウザでしたかったので
yarn add html-webpack-plugin -D
rem tsconfig 作る
yarn tsc --init
デコレータを使うのでtsconfig.json
の以下の部分の設定を変更します。
{
"compilerOptions": {
"experimentalDecorators": true,
"emitDecoratorMetadata": true
}
}
webpackの設定周りは省略します。
確認1 injectable
を試す
とりあえずシンプルに動かしてみます。
一番末端のクラス
import { injectable } from "tsyringe";
@injectable()
export default class FooService {
public constructor() {
console.log("new FooService()")
}
doFoo(value: string) {
return `${value} : Foo!`
}
}
FooService
を使うクラス
import { injectable } from "tsyringe";
import FooService from "./FooService";
@injectable()
export default class HogeService {
constructor(
private fooService: FooService
) {
console.log("new HogeService()")
}
doHoge(value: string) {
return `${value} : Hoge!`
}
doHogeFoo(value: string) {
return this.fooService.doFoo(this.doHoge(value))
}
}
エントリポイント
//Reflect API を使うためのPolyfill
import "reflect-metadata";
import { container } from "tsyringe";
import FooService from "./FooService";
import HogeService from "./HogeService";
//FooServiceを実行
console.log("---------")
const fooService = container.resolve(FooService);
console.log(fooService.doFoo("test01"));
//HogeServiceを実行
console.log("---------")
const hogeService = container.resolve(HogeService);
console.log(hogeService.doHogeFoo("test02"))
実行結果は以下のようになります。
---------
new FooService()
test01 : Foo!
---------
new FooService()
new HogeService()
test02 : Hoge! : Foo!
特に何も言うことはないです😀
確認1-2 FooService
の@injectable()
を消してみる
import { injectable } from "tsyringe";
//@injectable()
export default class FooService {
public constructor() {
console.log("new FooService()")
}
doFoo(value: string) {
return `${value} : Foo!`
}
}
実行結果は先ほどと同じになります。
---------
new FooService()
test01 : Foo!
---------
new FooService()
new HogeService()
test02 : Hoge! : Foo!
確認1-3 HogeService
の@injectable()
を消してみる
FooService
の@injectable()
は復活させておきます。
import { injectable } from "tsyringe";
import FooService from "./FooService";
//@injectable()
export default class HogeService {
constructor(
private fooService: FooService
) {
console.log("new HogeService()")
}
doHoge(value: string) {
return `${value} : Hoge!`
}
doHogeFoo(value: string) {
return this.fooService.doFoo(this.doHoge(value))
}
}
これを実行したところ以下のようにエラーになります。
---------
new FooService()
test01 : Foo!
---------
dependency-container.js:210 Uncaught Error: TypeInfo not known for "HogeService"
at InternalDependencyContainer.construct (dependency-container.js:210)
at InternalDependencyContainer.resolve (dependency-container.js:91)
以下略
確認1 からわかること
@injectable
をつけることで、コンテナからインスタンスを取得するときに、コンストラクタに依存パラメータがあれば、それも解決してくれるようです。
FooService
の@injectable
を消しても動いていたのは、何も依存するものがないからエラーが起きていなかったようです。
このあたりのことはinjectable.tsでパラメータ情報を登録している(のかな?)ことや
dependency-container.ts
の resolveメソッドのこのあたり およびconstructメソッド
を見れば確認ができます。
確認2 singleton
を試す
確認1
とほとんど同じですがFooService
のみ@injectable()
を @singleton()
に変更します。
import { singleton } from "tsyringe";
//↓ここを変更した。
@singleton()
export default class FooService {
public constructor() {
console.log("new FooService()")
}
doFoo(value: string) {
return `${value} : Foo!`
}
}
HogeService.ts
は変更なし(@injectable()
はつけています。)
index.ts
も変更なしです。
実行結果は以下のようになります。
---------
new FooService()
test01 : Foo!
---------
new HogeService()
test02 : Hoge! : Foo!
new HogeService()
の前にあった new FooService()
が消えています。
他のDIコンテナと同じように@singleton()
をつけると、コンテナ内で一つのインスタンスが再利用されるようです。
singleton.ts を見ると、コンテナにシングルトンとして登録しているようです。
確認3 autoInjectable
を試してみる
autoInjectable
を使うとコンテナから取り出すのではなくnew
するだけで依存関係を解決してくれるようです。(すげぇ)
import { injectable } from "tsyringe";
@injectable()
export default class FooService {
public constructor() {
console.log("new FooService()")
}
doFoo(value: string) {
return `${value} : Foo!`
}
}
@autoInjectable()
を付与したクラスです。注意点としてコンストラクタのパラメータに?
をつけます。
import { autoInjectable } from "tsyringe";
import FooService from "./FooService";
@autoInjectable()
export default class AutoHogeService {
constructor(
// ?をつけておかないと new AutoHogeService() とできずコンパイルエラーになる。
private fooService?: FooService
) {
console.log("new AutoHogeService()")
}
doHoge(value: string) {
return `${value} : Hoge!`
}
doHogeFoo(value: string) {
return this.fooService!.doFoo(this.doHoge(value))
}
}
import "reflect-metadata";
import AutoHogeService from "./AutoHogeService";
console.log("------");
const autoHogeService1 = new AutoHogeService();
console.log(autoHogeService1.doHogeFoo("test1"));
console.log("------");
const autoHogeService2 = new AutoHogeService();
console.log(autoHogeService2.doHogeFoo("test2"));
実行結果は以下のようになります。
------
new FooService()
new AutoHogeService()
test1 : Hoge! : Foo!
------
new FooService()
new AutoHogeService()
test2 : Hoge! : Foo!
new AutoHogeService()
を実行したときに new FooService()
も実行されているようです。
auto-injectable.ts を見ても何やらresolve
していますね。(詳細は解読できていないのでわかりません..)
おわりに
今回は injectable
, singleton
, autoInjectable
を試してみました。
長くなったのでこの辺で終わります。
続きはこちら