← Spring コース
47. NestJS の DI と何が違うか
NestJS も も、に型を書けば依存が渡されますが、読む場所が違います。TypeScript の型注釈は出力に残りません。旧式デコレータ(experimentalDecorators)と emitDecoratorMetadata を有効にすると、コンパイラがに design:paramtypes の1行を足し、NestJS はそれを読みます。Java では型が に残り、直接読めます。
この差はで表に出ます。TypeScript のインターフェースは書き出されると Object になり、どのインターフェースだったかは残りません。そのため NestJS では文字列や Symbol、をトークンにします。Java ではインターフェース型がそのまま読めるので、Spring はそれを鍵に実装を探せます。Spring でインターフェースで受けて実装を差し替える書き方が普通に使えるのは、このためです。
見える範囲も違います。NestJS は単位でプロバイダを囲い、exports で公開します。Spring の の識別子はの中で一意で、実測(Java のモジュール機能を使わない構成)では、別の でない実装クラスが宣言なしで注入されました。そのぶん、同じ型の Bean が2つあると、その型を1つだけ求める注入は絞り込めずに失敗します。
点線の付いた語はタップすると説明が出ます。用語集を開く →
TypeScript のソースconstructor(repo: CatsRepository)
Java のソースOrderService(PaymentGateway gateway)
コンパイル
出力の JavaScriptconstructor(repo) — 型注釈は無い
設定を有効にすると design:paramtypes の1行が足される.class引数の型がそのまま入っている
実行時に読む
書き足されたメタデータクラスはそのまま。インターフェースは Object
リフレクションインターフェースも型の名前で読める
コンテナが解決に使う鍵
NestJSクラス、または文字列・Symbol・抽象クラスのトークン
Springクラスまたはインターフェースの型
- ・左の列は TypeScript 7.0.2 の旧式デコレータ(experimentalDecorators)での実測です。NestJS 自体は起動していません
- ・NestJS の公式ドキュメント本文に design:paramtypes や emitDecoratorMetadata の語は見当たりませんでした(確認した7ページの範囲)。前者は NestJS のソース、後者は公式スターターの tsconfig.json にあります
- ・Java でも、List<String> の String のような型引数はオブジェクトには残りません。残るのはクラスそのものと、宣言に書いた型です
- ・どちらが優れているかではなく、言語が実行時に何を残すかの違いです
サンプルコード(フレームワーク環境が必要なため表示のみ)
// demo.ts — NestJS は入れず、TypeScript のコンパイラが何を書き出すかだけを見る
// tsconfig: "experimentalDecorators": true, "emitDecoratorMetadata": true
import "reflect-metadata";
// 中身が空のデコレータ。付いていることだけが意味を持つ
function Injectable(): ClassDecorator {
return () => {};
}
class CatsRepository {}
interface Clock {
now(): number;
}
@Injectable()
class WithClass {
constructor(private readonly repo: CatsRepository) {} // クラス型の引数
}
@Injectable()
class WithInterface {
constructor(private readonly clock: Clock) {} // インターフェース型の引数
}
class NoDecorator {
constructor(private readonly repo: CatsRepository) {} // デコレータなし
}
const show = (c: Function) =>
console.log(c.name.padEnd(14), Reflect.getMetadata("design:paramtypes", c));
show(WithClass);
show(WithInterface);
show(NoDecorator);
// TypeScript 7.0.2 / reflect-metadata 0.2.2 / Node 24.21.0 での出力
//
// $ node dist-on/demo.js (emitDecoratorMetadata: true)
// WithClass [ [class CatsRepository] ]
// WithInterface [ [Function: Object] ]
// NoDecorator undefined
//
// $ node dist-off/demo.js (emitDecoratorMetadata: false)
// WithClass undefined
// WithInterface undefined
// NoDecorator undefined
図やコードに出てくる用語(タップで説明)
サンプルコードで身につける(5本) →
学んだ概念を、素朴な例から実務寄りの例まで5つの具体例で確認します。
公式ドキュメントで詳しく ↗