メインコンテンツまでスキップ

第6章 超重要:TSは型が実行時に消える👻(ここがC#と違う!)

この章はDI(依存性注入)をTypeScriptでやるときの** 返ってくるのは 入力だけの関数(ここが最高に便利😍)

分かると、あとが一気にラクになる✨


🎯 この章のゴール(ここまでできたらOK!)

  • TypeScriptの型は、実行時に存在しない」を自分の言葉で説明できる🗣️✨ (TypeScript)
  • 「だからTSのDIはC#みたいに“型から自動解決”が基本できない」を理解できる🧠💡
  • 代わりに必要になる “値(token)” ベースのDI テストが爆速で安定🧪💕

1) まず結論:TSの型は“設計図”で、完成品(実行時)には残らない👻📄➡️📦

TypeScriptは JavaScriptに変換して実行されるよね。 このとき 型注釈(: string とか)や型の情報は消える。 公式ドキュメントでも「TypeScript固有のものは消える(eraseされる)」って説明されてるよ✨ (TypeScript)

たとえば👇

// ✅ TypeScript(書いてるときは型がある)
function greet(name: string) {
return "Hello, " + name;
}

出力されるJavaScriptはだいたいこう👇(イメージ)

// ✅ JavaScript(実行時。型はもういない👻)
function greet(name) {
return "Hello, " + name;
}

型は“コンパイル時のチェック用”で、動いてる最中の世界にはいないって覚えよう🧠✨ (型アサーションも実行時チェックはしてくれないよ〜⚠️) (TypeScript)


2) 「interfaceが実行時にいない」がDIで致命的になる理由💥💉

❌ やりがちな罠:interfaceをDIコンテナのキーにしたくなる

C#だと「ILogger を登録して…」みたいに、インターフェイスそのものを登録キーにできるよね。

でもTSで同じことをやろうとするとこうなる👇

interface Logger {
log(msg: string): void;
}

// ❌ これはできない(interfaceは“型”であって“値”じゃない)
container.register(Logger, ConsoleLogger);

だって Logger実行時に存在しないから、登録キーとして渡せないの🥲 この「型=消える」性質のせいで、TSのDIは基本的に “値(value)をキーにする” 発想が必要になるよ🔑✨


3) じゃあTSのD> I/Oは外側に追い出して、中心をPureにする✂️✨

TypeScriptでDIするときのキーはだいたいこのへん👇

✅ A. class(クラス)は実行時にいる🏛️

class ConsoleLogger {
log(msg: string) { console.log(msg); }
}

// クラス(コンストラクタ関数)は実行時に存在する=キーにできる
container.register(ConsoleLogger, () => new ConsoleLogger());

📌 ただし注意! 「抽象(interface)ではなく具象(class)に寄りがち」になって、DIP的にイヤなときがある😵‍💫


✅ B. string(文字列token)🧵

const LOGGER = "Logger";

container.register(LOGGER, () => new ConsoleLogger());

📌 注意! 文字列は衝突しやすい(同名の事故)🥲


✅ C. Symbol(おすすめ!衝突しない)💎

export const LOGGER = Symbol("Logger");
export type Logger = { log(msg: string): void };

container.register(LOGGER, () => new ConsoleLogger());

// 取り出すときは型引数で「これはLoggerだよ」を付ける
const logger = container.resolve<Logger>(LOGGER);
logger.log("hi!");

Symbolは実行時に存在する値だし、基本衝突しないから便利💎✨


4) 「import」も実行時に関係あるよ⚠️(型だけのつもりが地雷になるやつ)

ここ、DIと相性悪い事故ポイント🔥

import type は “型だけ” を読み込む(実行時には消える)👻📦

TypeScript公式のリリースノートでも、import type出力に残らず完全に消えるって明言されてるよ✨ (TypeScript)

import type { Logger } from "./logger-types"; // ✅ 型だけ

// これは実行時JSには残らない(= 余計な依存や副作用を増やしにくい)

DIでありがちな「循環import🌀」や「読み込んだ瞬間に副作用ドーン💥」を避けるのに効く👍


verbatimModuleSyntax:importの挙動を“素直”にする設定🧼

最近のTSでは、モジュール周りの事故を減らすために verbatimModuleSyntax みたいなオプションも用意されてるよ(import/exportをより明確に扱う)🧹✨ (TypeScript)

あと preserveValueImportsverbatimModuleSyntax のほうが今は推奨って扱いになってるよ📌 (TypeScript)


5) まとめ:この章がDIにくれる超大事な“合言葉”🧩✨

  • TSの型(RegisterDeps として成立してるかチェックしつつ、オブジェクトの“中身の型推論”は潰さないのが satisfies の良さだよ〜!(TypeScript)
  • だからTSのDIは「型から自動で解決!」よりも、まず 値token(Symbolとか)で組み立てる方向が現実的🔑
  • import type を使うと「型だけ」を安全に扱えて、DIの地雷(循環importなど)を踏みにくい💣➡️🧯 (TypeScript)

🧪 ミニ課題(5分)✨

課題A:一文で説明してみよ📝

「TypeScriptの型が実行時に消える」ってどういうこと? 👉 30文字くらいで説明してみて😊

課題B:DI用のtokenを作ってみよ🔑

Clocknow(): number)のtokenを Symbol で作ってみてね💎


🤖 AIにお願いするなら(コピペOK✨)

  • 「TypeScriptの型消失を、高校生にも分かる例えで3つ出して」
  • 「interfaceをDIのキーにできない理由を、短く説明して」
  • 「Symbol tokenを使った最小DI例をTypeScriptで書いて(Clock/Logger)」

📌 最新状況メモ(2026-01-16時点)

  • TypeScriptの最新安定版は 5.9.3(npmと公式リリース一覧で確認できるよ) (npm)
  • 公式ブログでは **TypeScript 6.0は5.9→7.0への“橋渡し”**という位置づけで、リリースは早期2026が目標と報じられてるよ (Microsoft for Developers)

(でもね!✨ バージョンが進んでも「型が実行時に消える」はTypeScriptの根っこなので、ここはずっと超重要だよ👻)


次の第7章からは、この前提の上で「じゃあ依存をどう渡す?(deps注入)」を、手を動かしながらやっていくよ〜👜💉✨