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. interface と type の使い分けミス
// 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 の型システムは強力ですが、使い方を誤ると型チェックの恩恵を受けられません。特に any と as の乱用は最も避けるべきパターンです。satisfies・as const・keyof typeof などのモダンな書き方を習得すると、型安全性と開発体験が大きく向上します。