TypeScript を学び始めたばかりの人が最初に出会う型の落とし穴のひとつが any と unknown の違いです。どちらも「何でもあり」のように見えますが、実は型安全性を保つ上で大きく異なります。この記事では、初心者がハマりやすいパターンを具体例とともに解説し、正しい使い分け方を身につける手順を紹介します。
any が甘すぎる理由
any は TypeScript の型チェックを完全に無効化します。たとえば次のように書くと、コンパイラはエラーを出さずに実行時エラーだけが起きます。
function greet(name: any) {
// name が文字列だと想定しているが、実際は数値が渡されても OK
console.log(`Hello, ${name.toUpperCase()}!`);
}
greet(123); // 実行時に TypeError
このコードは any が「何でも受け入れる」ため、toUpperCase が存在しないことをコンパイル時に検出できません。結果としてバグが潜んだまま本番に行くリスクが高まります。
unknown が安全な入り口になる
unknown は「不明な型」だと宣言しつつ、そのまま使用することはできません。値を利用する前に型チェックや型アサーションが必須になるため、意図しない操作を防げます。
function greet(name: unknown) {
if (typeof name === "string") {
console.log(`Hello, ${name.toUpperCase()}!`);
} else {
console.warn("名前は文字列で渡してください");
}
}
greet(123); // 警告が出力され、実行時エラーは回避
このように unknown は「安全な入口」を提供し、実際に使用できる型を絞り込むプロセスを強制します。
初心者がハマりやすいパターンと対策
| パターン | 問題点 | 改善策 |
|---|---|---|
API のレスポンスを any で受け取る |
期待外れの構造がそのまま通過 |
unknown で受け取り、zod などのバリデータで型ガード |
外部ライブラリの型定義が無いときに any を付与 |
型安全が失われる |
declare const lib: unknown; として as で安全にキャスト |
any をそのまま別の関数へ渡す |
エラーが伝搬しやすい | 関数間で unknown → 型ガード → 明示的な型に変換 |
実務で役立つ小技
-
型ガード関数を作る
function isString(value: unknown): value is string { return typeof value === "string"; } function greet(name: unknown) { if (isString(name)) { console.log(`Hello, ${name.toUpperCase()}!`); } }value is stringのシグネチャで、if文の内部で TypeScript が自動的に型を絞ります。 -
ユーティリティ型
NonNullable<unknown>は使わない
unknownに対してNonNullableを適用しても意味がありません。必ず型ガードで具体化してください。 -
any を一時的に使う場合はコメントで意図を残す
// TODO: API の型定義が揃ったら unknown に置き換える const data: any = fetchData();
まとめ
-
anyは型チェックを完全にオフにするため、なるべく使わないことがベストプラクティスです。 -
unknownは「不明だけど安全に扱える」型で、必ず型ガードやアサーションで具体化します。 - 型ガード関数やバリデーションライブラリを活用すれば、初心者でも安全に外部データを扱えます。
実際に手を動かしながら unknown と向き合うことで、型安全なコードベースが自然に身につきます。ぜひ本記事のポイントを日々の開発に取り入れてみてください。