Prompt

Tsで、その型が正しいと仮定した実装を進めるけど、
実行時にはjsに変換される性質から、実行時型検査が漏れるときがあるんじゃないの?
APIから送受信するJSONとか…

その懸念、まさに TypeScript(TS)を使い始めたエンジニアが最初にぶつかる、そして最も健全な 「不信感」 だよ!

結論から言うと、君の直感は 100% 正解 だ。TypeScript は 「静的解析時」 には最強の盾になるけれど、実行時にはその盾をすべて投げ捨てて全裸(素の JS)で戦場に立つ。この現象を情報工学的には 「Type Erasure(型消去)」 と呼ぶんだ。

特に API から届く JSON のような「外部からの不純物」に対して、TS の型定義はただの 「願望」 に過ぎない。還元主義的に、この問題の正体と解決策を解剖しよう!


1. 型消去(Type Erasure)という冷徹な現実

TS で interface User { id: number } と書いても、コンパイル後の JS には User という情報は 1 ミリも残らない。

  • コンパイル時: user.id.toFixed() を書けば、TS は「id は number だから OK!」と通してくれる。

  • 実行時: もし API が {"id": "abc"} (string) を返してきたら、JS は toFixed() を実行しようとして TypeError: user.id.toFixed is not a function で爆発する。

TS の型チェックはあくまで「プログラマが嘘をついていないか」を確認するものであって、「実行時のデータが正しいか」を保証するものではないんだ。


2. API 通信における「型の漏れ」を防ぐ 3 つの戦略

この「実行時の型漏れ」を防ぐために、現場では主に以下の 3 つのアプローチが取られているよ。

① Type Guard (ユーザー定義型ガード)

JS の標準機能を使って、実行時に「本当にこの型か?」を泥臭くチェックする関数を作る方法。

TypeScript

function isUser(obj: any): obj is User {
  return typeof obj.id === 'number';
}

const data = await response.json();
if (isUser(data)) {
  // ここから先だけ、TS は data を User 型として安全に扱う
  console.log(data.id.toFixed());
}

② バリデーションライブラリ (Zod / Superstruct)

今、TS 界隈で最も主流なのが Zod のようなライブラリを使う方法だ。

「型定義」と「実行時バリデーション」を一つにまとめられるのが最大の特徴だよ。

TypeScript

import { z } from 'zod';

const UserSchema = z.object({
  id: z.number(),
});

// 解析に失敗すると実行時にエラーを投げてくれる
const user = UserSchema.parse(await response.json());
// user は自動的に { id: number } 型として推論される!

③ OpenAPI / TypeSpec によるコード生成

バックエンド(Rust や Go など)の API 定義から、フロントエンドの TS 型定義とバリデータを自動生成する方法。

「API 仕様書と実装がズレる」という根本的な問題を、「単一の真実(Single Source of Truth)」 から生成することで解決するんだ。


3. なぜ TS は標準で実行時検査をしないのか?

Java や C# のような言語は実行時にも型情報(リフレクション等)を持っているけれど、TS があえてそれをしないのには理由がある。

  • パフォーマンス: すべての変数代入に型チェックを挟むと、JS の実行速度が劇的に落ちてしまう。

  • 互換性: あくまで「既存の JavaScript エコシステムに寄り添う」という設計思想があるからなんだ。


見落とされがちな視点:as (Type Assertion) の罠

君がコードの中で const user = data as User; と書いた瞬間、君は TS に対して 「俺を信じろ、この data は絶対に User 型だ。文句は言わせない」 と宣言したことになる。

これはコンパイラを黙らせる究極の手段だけど、実行時の型漏れを引き起こす最大の原因でもある。プロの間では as は極力使うな、バリデータを挟め」 というのが鉄則になっているよ。


結論

API からのデータは 「常に汚染されている」 と想定し、境界線(API 受信部)でバリデーションを挟んで 「TS の型を、実行時の真実にする」 手間が必要なんだね。

次は、「実際に Zod を使って、API から届く複雑な JSON を一撃で安全な TS 型に変換する実装」 をやってみる?それとも、「TS の unknown 型を活用して、型安全に未知のデータを扱う高度なテクニック」 について深掘りしてみる?

Would you like me to provide a practical code snippet showing how to integrate Zod with a fetch request in a Next.js project?