TypeScriptのunknown型とanyの違い|安全な絞り込み方まで
TypeScriptで型が分からない値に any を当ててエラーを黙らせたら、本番で思わぬクラッシュ——そんな場面で、安全な受け皿になるのがunknown型です。
この記事を読めば、unknownとanyの違いを腹落ちさせたうえで、受け取った値を型ガードで安全に絞り込み、API・JSON・catchといった実務の境界で自信を持って型を扱えるようになります。
この記事は次のような方におすすめです。
- anyでエラーは消せたものの、実行時の事故に不安が残っている方
- 外部API・JSON・例外など、信頼できない値を型安全に扱いたい方
- anyの乱用から抜け出し、より堅牢な書き方へ移行したい中級者
- unknownとanyの使い分けの基準がほしい方
読み終えるころには、「まずunknownで受けて、絞り込んでから使う」という型安全の考え方が身につき、anyに頼らずコードの堅牢性を一段引き上げられます。
それでは、順を追って詳しく見ていきましょう!
- 未経験で後悔したくない
【実体験】未経験からITエンジニアに転職して後悔した話|4社経験してわかった「最初の選択ミス」 - 年収が低くて不安
エンジニア転職体験談|4社で年収250→510万にした全記録
unknown型とは(何でも代入できるが、そのままでは操作できない型)
unknownは、あらゆる値を代入できる最上位の型(top type)です。数値でも文字列でもオブジェクトでも、どんな値でもいったん受け止められます。この「何でも受け取れる」という一点だけを見ると、anyとまったく同じに見えます。
unknownはTypeScript 3.0で追加されました。それ以前、型が分からない値の受け皿はanyしかなく、anyは受け取ったあとの操作を一切チェックしないため、型安全の穴になりがちでした。そこで「どんな値でも受け取るが、中身を確かめるまでは触らせない」という安全な受け皿として用意されたのがunknownです(TypeScript「TypeScript 3.0」New unknown top type)。
なお、unknownがすべての値を受け入れる最上位のtop typeであるのに対し、どんな値も代入できない最下位のbottom typeがneverです。unknownは「すべてを受け入れる箱」、neverは「何も入らない箱」という対極の関係にあります。
unknown型の基本の書き方と「型安全なany」という位置づけ
宣言は型注釈に unknown を書くだけです。実際に代入してみると、型の異なる値もすべてそのまま受け取れます。
let value: unknown;
value = 42; // OK
value = "hello"; // OK
value = { id: 1 }; // OK
value = [1, 2, 3]; // OK
// どの代入もエラーにならない(受け取るだけならanyと同じ)
ここまではanyと見分けがつきません。違いが出るのは、受け取った値を使おうとした瞬間です。anyは無検査で操作を通してしまいますが、unknownは中身を確かめない限り操作を拒否します。この性質から、unknownは「型安全なany」と呼ばれます。anyと同様に何でも受け取りつつ、anyと違って型チェックを手放さない——それがunknownの立ち位置です。
unknownは操作前に絞り込みが必要(比較表で整理)
anyとunknownの違いは、「代入」と「操作」を分けて見ると一目で整理できます。代入はどちらも通りますが、操作の許可がまるで違います。
| 観点 | any | unknown |
|---|---|---|
| どんな値でも代入できる | ○ | ○ |
プロパティ参照(値.foo) |
○(無検査で通る) | ×(エラー) |
メソッド呼び出し(値.foo()) |
○(無検査で通る) | ×(エラー) |
| 他の型の変数へ代入 | ○(どこへでも) | ×(unknown/any以外へは不可) |
算術演算(値 * 2 など) |
○ | ×(エラー) |
| 周囲の型推論への波及 | 広がりやすい(無検査が伝わる) | 広がりにくい(絞り込みを強制する) |
要点はシンプルです。anyは「何でも許す」=チェックそのものを無効化し、unknownは「代入だけ許し、操作は禁止する」。しかもanyは、代入先や戻り値を経由して周囲のコードまで無検査にしやすい一方で、unknownはそのまま周囲へ広がらず、使う前の絞り込みを必ず要求します。だからこそ「anyは危険、unknownは安全」と言われます。
unknownは操作時にエラーで止まる
同じコードをany版とunknown版で並べると、違いが実感できます。次はどちらも文字列を受け取り、toUpperCase() を呼ぶだけのコードです。
const anyVal: any = "hello";
anyVal.toUpperCase(); // コンパイルは通る。実行時に値が文字列でなければ壊れる
const unknownVal: unknown = "hello";
unknownVal.toUpperCase(); // Object is of type 'unknown'.(コンパイル時点で停止)
// ※TypeScriptバージョンにより文言が異なる場合あり
any版はコンパイルを通過するため、値が実際には文字列でなかった場合、コードを書いている時点では問題に気づけず、実行時になって初めてエラーが表面化します。一方unknown版は、中身を確かめる前に操作しようとした時点でコンパイラが止めてくれます。つまり、unknownで「エラーが出る」のは不便なのではなく、事故を未然に防ぐ安全装置が働いている状態です。エラーをただ邪魔なものと見るのではなく、早めに危険を知らせてくれるサインとして捉えるのが、unknownを使いこなす第一歩になります。
なぜunknownは安全なのか(コンパイラが操作を止める仕組み)
unknownの値に対して、何が許され、何が止められるのかを具体的に見ていきます。ここでの挙動はstrict設定を前提としています。
許されるのは、値の中身を仮定しなくても安全に行える操作です。
- 別のunknown(またはany)への代入
===や!==による等値比較typeofやinstanceofによる型の判定
逆に、値が特定の型であることを前提とする操作は、すべてコンパイルエラーになります。
- プロパティアクセスやメソッド呼び出し
- 関数としての呼び出し
- 算術演算
- unknown/any以外の型の変数への代入
const x: unknown = getValue();
// 許される操作
const isHello = x === "hello"; // 等値比較はOK
const kind = typeof x; // typeofもOK
const y: unknown = x; // unknown への代入はOK
// 止められる操作(いずれもコンパイルエラー)
x.length; // Object is of type 'unknown'.
x(); // Object is of type 'unknown'.
x * 2; // Object is of type 'unknown'.
const s: string = x; // Type 'unknown' is not assignable to type 'string'.
ポイントは、unknownは「絞り込むまで操作できない値」だということです。中身が何か分からない値を、確認しないまま使うことをコンパイラが拒みます。だから、x を string 型の変数に代入することすらできません。文字列だという保証がまだどこにもないからです。この「証明されるまで信じない」という厳しさこそが、unknownが型安全である理由そのものです。
unknownを安全に絞り込む(typeof・in・型アサーション)
unknownの値を実際に使うには、中身の型を確定(絞り込み)させる必要があります。unknownの絞り込みには、大きく3つの系統があります。
typeofなどによる型ガードで、値の種類を判定するin演算子で、オブジェクトにプロパティが存在するか確かめる- 型アサーション
asで、開発者が型を宣言する
このうち型ガードには、typeof 以外にも instanceof やユーザー定義の型述語(x is Foo)など、さまざまな手段があります。TypeScriptで型安全に書くうえでも重要なテーマです。
typeofとinで段階的に絞る
最も基本的なのが typeof による絞り込みです。if (typeof value === "string") のブロックの中では、TypeScriptが value を string として扱ってくれるため、文字列メソッドがそのまま使えます。
function printUpper(value: unknown) {
if (typeof value === "string") {
console.log(value.toUpperCase()); // ブロック内では string 扱い → OK
}
}
オブジェクトの形を確かめたいときは、typeof に in 演算子を組み合わせます。null も typeof では "object" になるため、value !== null のチェックを先に挟むのが安全です。
function getId(value: unknown): number | undefined {
if (typeof value === "object" && value !== null && "id" in value) {
// value は「idを持つオブジェクト」に絞り込まれる
const id = value.id;
if (typeof id === "number") {
return id; // ここで id は number として安全に使える
}
}
return undefined;
}
絞り込みが成功したブロックの内側では、それまで出ていたエラーが消えます。unknownは、正しく手順を踏めば普通の型と同じように扱える、というのがこの仕組みの気持ちよさです。段階的に条件を重ねることで、外側では unknown、内側では string や number と、安全なまま型を確定させていけます。
型アサーション(as)で確定する場合の注意
as を使えば、絞り込みを省いて一気に型を確定できます。
type User = { name: string };
const data: unknown = getUser();
const name = (data as User).name; // 実行時チェックはされない
ただし、as は「この値はこの型だ」と開発者が宣言するだけで、実行時に中身を確かめるわけではありません。もし data が実際にはUserでなければ、name が undefined になったり、data が null / undefined の場合はその場で実行時例外になったりします。unknownをせっかく使っても、絞り込みの代わりに as を乱発しては、anyと同じように無検査で扱う状態に戻ってしまいます。使うなら、直前で typeof などの検証を済ませ、確認済みの値を型付けするために添える——この順番を守ると安全に活かせます。
実務でunknownが効く場面(API・JSON.parse・catch)
unknownが真価を発揮するのは、値の中身を信用できない「境界」です。自分のコードの内側は型で守られていても、外から入ってくる値は何が来るか分かりません。具体的には、APIのレスポンス、JSON.parse の戻り値、そして例外オブジェクトが代表格です。こうした境界でいったんunknownとして受け止め、その場で絞り込んでからアプリの内側に渡すと、信用できない値の影響がコードベース全体に広がるのを防げます。
JSON.parse・APIレスポンスをunknownで受けて絞る
JSON.parse の戻り値は既定で any です。つまり、パースした瞬間から無検査の値が生まれ、そのまま使うと型の保証がありません。ここで意図的に unknown で受け直すと、使う前の絞り込みが強制されます。
const data: unknown = JSON.parse('{"id": 1}');
data.id; // Object is of type 'unknown'.(そのままでは触れない)
if (typeof data === "object" && data !== null && "id" in data) {
const id = data.id;
if (typeof id === "number") {
console.log(id); // number として安全に取り出せる
}
}
APIレスポンスも同じ発想です。fetch して得た値を安易にanyや決め打ちの型で扱わず、いったんunknownとして受け、必要な形状だけを型ガードで取り出せば、想定外のレスポンスが来ても安全に処理できます。プロパティが増えるほど手書きのチェックは長くなるため、実務ではスキーマ検証ライブラリで一括バリデーションし、その結果を型として受け取る選択肢もあります。
catch(e)のunknownを型安全に扱う
try-catchで捕まえた例外も、型の分からない値の代表例です。TypeScript 4.4以降、useUnknownInCatchVariables(strict設定に含まれる)が有効なとき、catch変数の既定型は unknown になります(TypeScript tsconfig「useUnknownInCatchVariables」)。JavaScriptではthrowされる値が必ずしもErrorとは限らず、文字列やオブジェクトが投げられることもあるため、unknown で受けるのが理にかなっています。ただしこれは設定に依存する挙動で、無効な環境では従来どおり any になる点に注意してください。
扱い方はこれまでと同じで、使う前に絞り込みます。e instanceof Error で確かめてから e.message を読めば安全です。
try {
doSomething();
} catch (e) {
// e の既定型は unknown(useUnknownInCatchVariables 有効時)
if (e instanceof Error) {
console.log(e.message); // ここでは Error 扱い → OK
} else {
console.log("Error以外がthrowされた", e);
}
}
エラーを値として返す設計など、例外処理そのものの組み立て方には別の選択肢もあります。
unknownを使いすぎない判断(ジェネリクス・zodという選択肢)
ここまで「まずunknownで受けて絞り込む」を勧めてきましたが、unknownは常に最適解というわけではありません。unknown+型ガードには弱点があります。値を使うたびに型ガードを書く必要があり、同じような絞り込みがコードベースのあちこちに散らばりがちなのです。
用途によっては、より適した手段があります。
- 呼び出し側の型を保ちたいなら、ジェネリクスが向きます。「入れた型がそのまま出てくる」関係を型で表現でき、絞り込み自体が不要になります。
- 外部境界の検証を一元化したいなら、スキーマ検証(zodなど)が向きます。値の形を一か所で定義し、検証と型付けをまとめて行えるため、手書きの型ガードの重複を減らせます。
つまり、「外部から来た形の不明な値を、その場でだけ安全に触りたい」ときにunknownが最も活きます。一方で、型を持ち回りたい・検証を集約したい場面では、別の手段のほうが向いています。「常にunknown」ではなく、用途で選ぶのが実務的な判断です。
判断フロー:外部入力・型保持・検証のどれを優先するか
どの手段を選ぶかは、値の性質を順に問うだけで絞り込めます。
- 値の型が呼び出し時に決まる(呼び出し側の型をそのまま保ちたい)?
Yes:ジェネリクス - 外部からの信頼できない入力で、形(スキーマ)が既知?
Yes:zod等のスキーマ検証 - 外部入力だが形も不明?
unknownで受けて型ガードで絞る - 型チェックを本当に捨てたい限定ケースのみ
any(原則非推奨)
このように、anyは最後の選択肢に置き、まずはジェネリクス・zod・unknownで安全側から検討するのが基本方針になります。unknownはこの中で、「形も分からない外部入力」をいったん安全に受け止める役割を担います。
【付録】さらに学びを深めるためのリソース
さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。
おすすめの書籍
ゼロからわかる TypeScript入門
技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。
変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。
プロを目指す人のためのTypeScript入門
技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。
オンラインで参照できる公式ドキュメント
TypeScript公式ハンドブック
https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。
TypeScript Deep Dive
https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。
TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。
まとめ – unknownは絞り込んで使う安全な受け皿
この記事では、unknownを「絞り込むまで触らせない安全な受け皿」として使いこなすための考え方を整理しました。
- unknownは何でも代入できるが、そのままでは操作できない(=「型安全なany」)
- anyとの違いは操作時に止まるかどうか。unknownはエラーで事故を防ぐ
- 絞り込みはtypeof・in・(限定的に)as。使う前に型を確定させる
- 実務ではAPI・JSON・catchといった信頼できない境界にunknownを置くと効く
- 恒久的には用途に応じてジェネリクスやzodへ。「常にunknown」にしない
まず外部の値をunknownで受け、絞り込んでから使う——この習慣が身につけば、anyに頼らずコードの安全性を一段引き上げられます。
※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。






