TypeScriptで型が分からない値を受け取るとき、anyとunknownはどちらも「何が入るか分からない」を表せます。ただし使う側の安全性が違います。anyはプロパティ参照や関数呼び出しをそのまま許し、型チェックを弱めます。unknownは値を受け取れる一方、typeofやinstanceof、型ガードなどで絞り込むまで操作できません。APIレスポンスや外部入力の境界では、まずunknownとして受けて確認してから使う設計が判断しやすいです。
anyとunknownの違いを先に比較する
| 比較 | any | unknown |
|---|---|---|
| どんな値を代入できるか | ほぼ何でも可 | 何でも可 |
| プロパティやメソッドをすぐ使えるか | 使える。型チェックは効きにくい | 型を絞り込むまで使えない |
| 外部入力の境界 | 安全性を落とす意図が明確な場合に限定 | まず受け取り、検証して使う用途に向く |
| 移行・互換性 | 既存JavaScriptや型情報不足の逃げ道として使える | 既存コードへ確認処理を追加できる場合に向く |
unknownは「安全なany」と呼ばれることがありますが、単に置き換えれば終わりではありません。unknownを選ぶと、値を使う場所で型を確認する設計が必要になります。
anyは便利だが型チェックを外しやすい
function printName(value: any) {
console.log(value.user.name.toUpperCase());
}
printName({ user: null });
このコードはvalueの中身をTypeScriptが十分に検査できないため、コンパイル時に危険な参照を見逃しやすくなります。実行時に想定外の値が来るとエラーになる可能性があります。
anyを使うこと自体が常に誤りではありません。JavaScriptからTypeScriptへ段階移行する場合や、型情報のない外部ライブラリとの接続など、型チェックを意図的に緩める境界では選択肢になります。重要なのは、影響範囲を広げないことです。
unknownは使う前に型を絞り込む
function formatValue(value: unknown) {
if (typeof value === 'string') {
return value.toUpperCase();
}
if (typeof value === 'number') {
return value.toFixed(2);
}
return 'unsupported';
}
unknownの値には、そのまま文字列メソッドや数値メソッドを呼べません。typeofなどで型を絞り込むと、その分岐の中ではTypeScriptが具体的な型として扱えるようになります。
用語解説:Narrowing(型の絞り込み)
条件分岐や型ガードを使って、広い型から実際に扱える具体的な型へ絞るTypeScriptの仕組みです。
APIやJSONはunknownとして境界を作る
外部から来るデータは、TypeScriptの型注釈だけで実際の値が保証されるわけではありません。JSONを読み取った後にunknownへ受け直し、必要な形かを確認してからアプリ内部へ渡すと境界が明確になります。
type User = {
id: number;
name: string;
};
function isUser(value: unknown): value is User {
if (typeof value !== 'object' || value === null) return false;
const candidate = value as Record<string, unknown>;
return typeof candidate.id === 'number'
&& typeof candidate.name === 'string';
}
const raw: unknown = JSON.parse(responseText);
if (isUser(raw)) {
console.log(raw.name.toUpperCase());
}
ここで重要なのは、JSON.parseを呼んだだけでデータが検証済みになるわけではないことです。unknownのまま境界に置き、isUserのような確認を通してから内部ロジックへ渡します。
catchのエラーでもunknownが役立つ
try {
await loadData();
} catch (error) {
if (error instanceof Error) {
console.error(error.message);
} else {
console.error('Unknown error');
}
}
JavaScriptではErrorオブジェクト以外の値もthrowできます。そのためTypeScriptのstrict設定では、catch変数をunknownとして扱う仕組みが有効になります。messageへ直接触る前にinstanceof Errorなどで確認します。
anyとunknownを選ぶ判断基準
- 値の正体を確認してから使えるならunknownを優先する
- API・JSON・ユーザー入力など外部境界ではunknownから検証する
- JavaScript移行や型のない互換レイヤーで一時的にチェックを外す必要があるならanyを局所化する
- unknownを型アサーションだけで即座に目的型へ変換し続けるなら、実際の検証が抜けていないか確認する
「anyを0件にする」こと自体を目的にする必要はありません。どこで型の保証が始まるのかを明確にし、保証できない値を内部へ無検証で流さないことが重要です。
関連するTypeScriptの型設計
TypeScriptの環境構築から確認したい場合は入門記事、非同期処理の戻り値を型付けしたい場合はPromise型の記事、定数から型を作る方法はkeyof typeofの記事につながります。
(TypeScriptの開発環境を整えるについては『TypeScriptの始め方|Node.jsとVSCodeで学ぶ開発環境構築ガイド』をご参照ください)
(Promiseの型定義でanyを減らすについては『TypeScript Promise型定義解説|any撲滅と使い分け』をご参照ください)
(定数から型を作るについては『TypeScript型定義自動化「keyof typeof」実践例と二重管理解消法』をご参照ください)
よくある疑問
unknownには何も代入できない?
いいえ。さまざまな値をunknownへ代入できます。制限されるのは主に、その値を具体的な型として利用する段階です。
anyは完全に禁止したほうがよい?
一律禁止とまでは言えません。既存JavaScriptの移行や型情報が不足する境界などで使う場合は、範囲を局所化し、内部へanyを伝播させない設計を検討します。
unknownをasで型変換すれば型ガードは不要?
型アサーションは実データを検証しません。外部入力の形を保証したい場合は、実行時チェックや検証ライブラリなど別の確認が必要です。
まとめ
anyとunknownは、どちらも型が分からない値を扱えます。ただしanyは型チェックを緩め、unknownは確認するまで利用を制限します。外部データはunknownで受け、Narrowingや型ガードを通してから内部へ渡すと、型の保証範囲が見えやすくなります。anyが必要な場合も、その範囲を小さく保つのが実務的です。
まずAPIやJSONを受け取っている箇所を一つ選び、anyが内部へ広がっていないか確認してみてください。