SJ blog
frontend
S

信頼度ランク

S 公式ソース確認済み
A 成功実績多数・失敗例少数
B 賛否両論
C 動作未確認・セキュリティリスク高
Z 個人所感

TypeScriptで陥りやすい型の罠10選

中級者がよくハマるTypeScriptの型エラーとその解決策。any濫用・型の絞り込み失敗・as const・satisfies演算子など実践的なパターンを解説します。

一言結論

TypeScriptのanyとasは「型チェックをやめる宣言」であり、unknownと型ガード・satisfies演算子・as constに置き換えるだけで、コンパイルは通るのにランタイムで壊れるコードの大半を事前に検出できる。

1. any を使ってすべてを諦める

any は TypeScript の型チェックをすべてバイパスします。「とりあえず動かす」ために使いがちですが、バグの温床になります。

// ❌ 悪い例
function process(data: any) {
  return data.user.name; // ランタイムエラーになっても気づけない
}

// ✅ 良い例
function process(data: unknown) {
  if (typeof data === "object" && data !== null && "user" in data) {
    // 型ガードで安全にアクセス
  }
}

2. as キャストの乱用

型アサーション as は「私が保証するから型チェックをスキップして」という命令です。

// ❌ 危険
const user = fetchData() as User; // fetchData が null を返しても通る

// ✅ 安全
const user = fetchData();
if (user && isUser(user)) { // 型ガード関数で検証
  console.log(user.name);
}

3. Union 型の絞り込み失敗

type Shape = { kind: "circle"; radius: number } | { kind: "square"; side: number };

function area(s: Shape) {
  // ❌ s.radius は直接アクセスできない
  // ✅ Discriminated Union で絞り込む
  if (s.kind === "circle") {
    return Math.PI * s.radius ** 2; // ここでは radius が確定
  }
  return s.side ** 2;
}

4. as const を忘れる

// ❌ これは string[] として推論される
const COLORS = ["red", "green", "blue"];

// ✅ as const でリテラル型のタプルになる
const COLORS = ["red", "green", "blue"] as const;
// 型: readonly ["red", "green", "blue"]

type Color = typeof COLORS[number]; // "red" | "green" | "blue"

5. オブジェクトの型推論が広すぎる

// ❌ { direction: string } として推論される
const config = { direction: "left" };

// ✅ satisfies で型チェックしつつリテラル型を保持
const config = { direction: "left" } satisfies { direction: "left" | "right" };
// config.direction の型は "left"(string ではない)

6. null / undefined チェックの漏れ

strictNullChecks を有効にしていると、null や undefined の可能性がある値を直接使えなくなります。

// ❌ element が null の可能性を無視
document.getElementById("app").innerHTML = "hello";

// ✅ オプショナルチェーン + Null 合体
document.getElementById("app")?.innerHTML ?? "default";

// ✅ または Non-null assertion(確信がある場合のみ)
const el = document.getElementById("app")!;

7. ジェネリクスを恐れて重複コードを書く

// ❌ 同じロジックを型ごとに書く
function firstString(arr: string[]): string | undefined { return arr[0]; }
function firstNumber(arr: number[]): number | undefined { return arr[0]; }

// ✅ ジェネリクスで統一
function first<T>(arr: T[]): T | undefined { return arr[0]; }

8. keyof / typeof の使い忘れ

const STATUS = { active: 1, inactive: 2, pending: 3 } as const;

// ❌ 手動でリテラル型を書く
type StatusKey = "active" | "inactive" | "pending";

// ✅ keyof typeof で自動生成
type StatusKey = keyof typeof STATUS; // "active" | "inactive" | "pending"

9. 関数のオーバーロードが適切でない

// ❌ 引数で分岐する関数に union 型を使う
function format(val: string | number): string | number { ... }

// ✅ オーバーロードシグネチャで戻り値型を確定させる
function format(val: string): string;
function format(val: number): number;
function format(val: string | number): string | number {
  return typeof val === "string" ? val.trim() : Math.round(val);
}

10. interfacetype の使い分けミス

// interface: extends・宣言マージができる(クラスやオブジェクト形状に向く)
interface User { id: number; name: string; }
interface AdminUser extends User { role: "admin"; }

// type: Union・交差型・Mapped Type などに向く
type ID = string | number;
type Nullable<T> = T | null;
type ReadonlyUser = Readonly<User>;

まとめ

TypeScript の型システムは強力ですが、使い方を誤ると型チェックの恩恵を受けられません。特に anyas の乱用は最も避けるべきパターンです。satisfiesas constkeyof typeof などのモダンな書き方を習得すると、型安全性と開発体験が大きく向上します。


参考: TypeScript公式ドキュメント