TypeScriptを導入する大きな目的の一つは、静的型付けによる堅牢なコード開発を実現することです。
しかし、開発を進める中で、どうしても変数の型が事前には特定できないケースに直面することがあります。
このような場面で頻繁に登場するのがany型とunknown型ですが、これら二つの性質は大きく異なります。
型安全性を損なわずに柔軟なコードを書くためには、それぞれの違いを正確に理解し、適切に使い分けることが不可欠です。
本記事では、2026年現在の開発現場でも重視されている、anyとunknownの根本的な違いと具体的な使い分けのポイントについて詳しく解説します。
TypeScriptにおける「型が不明」な状態とは
プログラムを実行するまで、ある変数がどのような値を持つのか確定しない場面は珍しくありません。
例えば、外部APIから取得したデータや、ユーザーからの入力を受け取る変数がその典型例です。
TypeScriptでは、このような「何でも入り得る型」を表現するために、いくつかの選択肢を提供しています。
その中で最も代表的なものが、古くから存在するany型と、TypeScript 3.0で導入されたunknown型です。
初心者のうちは、エラーを回避するためにanyを多用してしまいがちですが、これはTypeScriptの恩恵を放棄することに繋がります。
モダンな開発においては、可能な限りunknownを使用し、型安全なコードを維持することが推奨されています。
any型の性質と潜んでいるリスク
any型は、文字通り「どのような型でも受け入れる」型です。
TypeScriptのコンパイラは、any型の変数に対して型チェックを完全に行わなくなります。
any型の最大の特徴
any型の最大の特徴は、その変数に対してどのような操作も許可されるという点にあります。
プロパティへのアクセス、関数の実行、別の型への代入など、すべてが無審査で通ってしまいます。
これはJavaScriptの挙動に非常に近く、開発速度を優先したい場合には一見便利に感じられます。
any型が引き起こすランタイムエラー
しかし、anyの使用はコンパイル時のチェックを無効化するため、実行時に深刻なエラーを引き起こす原因となります。
以下のコード例を見てみましょう。
// any型を使用した場合
let someValue: any = "Hello, TypeScript";
// 数値型ではないのに、数値型のメソッドを呼び出せてしまう
someValue.toFixed(2);
// コンパイルエラーは発生しないが、実行時にエラーとなる
Uncaught TypeError: someValue.toFixed is not a function
このように、anyを使用すると、本来防げるはずのミスが、実行時まで表面化しないというリスクを抱えることになります。
「型安全な開発」というTypeScriptの存在意義を根底から揺るがすため、無計画なanyの使用は避けるべきです。
unknown型の性質と安全性の仕組み
一方のunknown型は、anyと同様に「どのような値でも代入できる」という性質を持ちますが、その後の扱いが大きく異なります。
unknownは「型は不明だが、安全に使用するために事前の確認が必要」という制約を課す型です。
unknown型の厳格な制約
unknown型の変数に対しては、そのままメソッドを呼び出したり、特定のプロパティにアクセスしたりすることはできません。
開発者はその変数が何であるかを「証明」しない限り、コンパイラから操作の許可を得られない仕組みになっています。
この仕組みにより、開発者は予期せぬ実行時エラーから保護されます。
// unknown型を使用した場合
let someValue: unknown = "Hello, TypeScript";
// 直接メソッドを呼ぼうとするとコンパイルエラーになる
// someValue.toFixed(2); // Error: 'someValue' is of type 'unknown'.
// 型を確認(絞り込み)してから操作する
if (typeof someValue === "number") {
// このブロック内では number型として扱える
console.log(someValue.toFixed(2));
} else if (typeof someValue === "string") {
// このブロック内では string型として扱える
console.log(someValue.toUpperCase());
}
HELLO, TYPESCRIPT
このように、unknown型は「使用前に型を絞り込む」ことを強制するため、コードの信頼性が飛躍的に高まります。
anyとunknownの決定的な違い
ここで、anyとunknownの違いを表で整理してみましょう。
| 項目 | any型 | unknown型 |
|---|---|---|
| どのような値も代入できるか | 可能 | 可能 |
| 型チェックの有無 | なし(すべて許可) | あり(厳格にチェック) |
| メソッド呼び出し等の操作 | 自由に行える | 型を特定するまで不可 |
| 他の型への代入 | 可能(危険) | 型を特定するまで不可 |
| 安全性 | 低い(JavaScriptと同等) | 高い |
この表から分かる通り、anyは「型の放棄」であり、unknownは「安全な不特定型」であると言えます。
一言で言えば、「何でも許される自由」がanyであり、「責任を持って扱う必要がある」のがunknownです。
型安全な絞り込み(Type Narrowing)の手法
unknown型を効果的に活用するためには、TypeScriptが提供する「型の絞り込み」機能を使いこなす必要があります。
具体的な手法をいくつか紹介します。
1. typeof 演算子による絞り込み
プリミティブ型(string, number, booleanなど)を判定する際に最も基本的な手法です。
先ほどの例でも示した通り、条件分岐の中でtypeofを使用することで、特定のブロック内だけ安全に型を扱えます。
2. instanceof 演算子による絞り込み
クラスのインスタンスかどうかを判定する際に使用します。
class ApiError extends Error {
code: number = 500;
}
function handleError(error: unknown) {
if (error instanceof ApiError) {
// error は ApiError 型として認識される
console.error(`Error Code: ${error.code}`);
} else if (error instanceof Error) {
// error は 標準の Error 型として認識される
console.error(error.message);
}
}
3. ユーザー定義の型ガード(Type Guards)
より複雑なオブジェクトの構造をチェックしたい場合に有効な手法です。
isキーワードを使用して、特定の型であることを保証する関数を定義します。
interface User {
id: number;
name: string;
}
// ユーザー定義型ガード関数
function isUser(obj: unknown): obj is User {
const u = obj as User;
return (
u !== null &&
typeof u === "object" &&
typeof u.id === "number" &&
typeof u.name === "string"
);
}
function processUserData(data: unknown) {
if (isUser(data)) {
// ここでは data が User 型として扱える
console.log(`User Name: ${data.name}`);
} else {
console.log("Invalid user data");
}
}
このように、型ガードを実装することで、外部から来た不確かなデータに対しても完璧な型安全性を確保できます。
実践的な使い分けのガイドライン
どのような基準でこれらを使い分ければ良いのでしょうか。
2026年のモダンな開発基準に基づいた推奨ルールを解説します。
基本的にunknownを第一選択にする
型が不明な値を扱う際、デフォルトで選ぶべきなのはunknownです。
API通信の結果を受け取る際や、汎用的なユーティリティ関数を作成する場合は、まずunknownで型を定義しましょう。
コンパイルエラーが出ることで、「ここで型の確認が必要だ」という気づきを得られるのが大きなメリットです。
anyを使用しても許容されるケース
とはいえ、anyを完全に排除するのは難しい場合もあります。
例えば、既存のJavaScriptプロジェクトをTypeScriptへ移行する初期段階では、一時的にanyを多用せざるを得ないでしょう。
また、型の定義があまりにも複雑になりすぎてしまい、コードの可読性を著しく損なう場合も、例外的な手段としてanyが検討されます。
ただし、その場合でも// eslint-disable-next-line @typescript-eslint/no-explicit-anyのようなコメントを残し、「意図的にanyを使っている」ことを明示するのがマナーです。
外部ライブラリとの連携
使用している外部ライブラリが古い、あるいは型定義ファイルが不正確な場合、型定義を合わせるためにanyが必要になることがあります。
その際も、外部との境界線だけでanyを使用し、自分のアプリケーションの内部ロジックには持ち込まないように工夫しましょう。
境界線で適切に型を変換(キャスト)することで、汚染を最小限に抑えられます。
開発効率と安全性のバランス
anyは開発スピードを一時的に向上させますが、長期的にはメンテナンスコストを増大させます。
バグの温床になりやすく、リファクタリングも困難になるからです。
対してunknownは、コードを書く際にわずかな手間(型チェックの記述)を強いますが、将来の安心感をもたらします。
「自分一人で書いているから大丈夫」という考えは、規模が大きくなるにつれて破綻します。
未来の自分や他のチームメンバーのために、unknownを選択し、契約に基づいたプログラミングを行う姿勢が重要です。
JSONパースにおける実践例
具体的な実践例として、JSON.parseの結果を扱う場面を考えてみましょう。
JSON.parseの戻り値は、TypeScriptの標準型定義ではanyになっていますが、これは非常に危険な仕様です。
const jsonString = '{"id": 1, "username": "tech_writer"}';
// JSON.parse は any を返すため、どのような操作も許されてしまう
const rawData = JSON.parse(jsonString);
console.log(rawData.nonExistentMethod()); // 実行時エラーの危険
// unknown に一度代入することで、安全な取り扱いを強制する
const safeData: unknown = JSON.parse(jsonString);
// safeData.id; // エラーが発生し、チェックを促される
このように、「anyを返す関数の戻り値をあえてunknownとして受け取る」というテクニックは、非常に有効な防衛策となります。
まとめ
本記事では、TypeScriptにおけるany型とunknown型の違い、そして安全な使い分けのポイントについて解説しました。
any型はあらゆるチェックを無効化する強力な武器ですが、同時にコードの堅牢性を壊す諸刃の剣でもあります。
一方でunknown型は、開発者に適切な型確認を促すことで、実行時の安全性を強力にバックアップしてくれます。
「型がわからないときは、まずunknown」というルールを徹底することで、より品質の高いTypeScriptコードが書けるようになるはずです。
近年のトレンドとしても、ESLintの設定でno-explicit-anyを有効にし、anyの利用を厳格に制限するプロジェクトが増えています。
ぜひ、今回紹介した型ガードなどの手法を活用し、型安全なTypeScript開発を実践してみてください。
