TypeScript で「型がはっきり分からない値」を扱うとき、any と unknown が候補に挙がります。
どちらも何でも代入できる型ですが、代入した後に何ができるかがまったく違います。
この記事では両者の違いと、使い分けを整理します。
any — 型チェックを無効化する型
any を付けた値には、どんな操作でもエラーなく通ります。
const value: any = "hello";
value.toUpperCase(); // OK
value.notFoundMethod(); // OK(実行時エラー)
value.foo.bar.baz; // OK(実行時エラー)
TypeScript は何も警告してくれません。
つまり any は 「分からないけど、何でもできることにする」 型です。
any の注意点
any が厄介なのは、関数の戻り値を通じて他の変数にも広がる 点です。
1 箇所の any がコードベース全体に波及し、型チェックが機能しない領域がじわじわ広がります。
function getConfig(): any {
return JSON.parse("...");
}
const config = getConfig(); // config も any
const name = config.user.name; // name も any
unknown — 確認するまで触らせない型
unknown も何でも代入できますが、そのままでは操作できません。
const value: unknown = "hello";
value.toUpperCase(); // コンパイルエラー
使うには、型を絞り込む必要があります。
if (typeof value === "string") {
value.toUpperCase(); // OK
}
unknown は 「分からないので、確認できるまで触らせない」 型です。
型の絞り込み方
unknown を扱うための代表的な絞り込み方法を整理します。
typeof によるプリミティブの判定
function formatValue(value: unknown): string {
if (typeof value === "string") return value;
if (typeof value === "number") return value.toString();
return "不明な値です";
}
instanceof によるクラスの判定
try {
// 何らかの処理
} catch (error: unknown) {
if (error instanceof Error) {
console.error(error.message);
}
}
ユーザー定義型ガード
オブジェクトの構造を検証したいときは、型ガード関数を使います。
type User = { name: string; age: number };
function isUser(v: unknown): v is User {
return (
typeof v === "object" &&
v !== null &&
"name" in v &&
typeof (v as User).name === "string" &&
"age" in v &&
typeof (v as User).age === "number"
);
}
const data: unknown = await fetchSomeData();
if (isUser(data)) {
console.log(data.name); // OK: User として扱える
}
APIレスポンスのように構造が不確定なデータに対しては、この方法が実用的です。
※ただし、ユーザー定義型ガードを自分で書くのは危険です。型述語 v is T の中身が間違っていてもコンパイラは検知してくれず、型定義を変えても追従漏れに気づけません。基本的にはスキーマライブラリ(Zod / Valibot など)を使うか、TypeScript の型推論に任せる方が安全です。
参考:
https://zenn.dev/yumemi_inc/articles/ts-avoid-hand-writing-type-guards
unknown が活きる場面
外部からのデータ取得
const response: unknown = await fetchSomeData();
型ガードで確認してから使えば安全です。
catch のエラー
TypeScript では throw に何でも渡せるため、catch で受け取る値は Error とは限りません。
catch (error: unknown) {
if (error instanceof Error) {
console.error(error.message);
}
}
汎用的な関数の入力
引数の型を限定したくないが、安全に扱いたい場面で有効です。
any を使ってよい場面
any は禁止するべきものではなく、スコープを限定して使う のがポイントです。
- 型定義がない古いライブラリを一時的に扱う
- JavaScript からの段階的な移行中
- 型で表現しきれず、一時的に逃がしたい
ただし、入口で any にしたらすぐ具体的な型に変換し、外に漏らさないことが大事です。
// ❌ any のまま使い回す
const data: any = getLegacyData();
doSomething(data); // data の any が伝播する
// ✅ 早めに型を付ける
const raw: any = getLegacyData();
const data: UserConfig = raw as UserConfig; // ここから先は型が効く
避けたいアンチパターン:ダブルアサーション
const value = someData as unknown as TargetType;
as unknown as T は型システムを完全にバイパスする書き方です。
本来通らない型変換を無理やり通すので、実行時エラーの温床になります。
これを書きたくなったら、型設計自体を見直すサイン と考えた方がよいです。
まとめ
any |
unknown |
|
|---|---|---|
| 代入 | 何でも入る | 何でも入る |
| 操作 | 何でもできる | 絞り込まないとできない |
| 型チェック | 無効化される | 維持される |
| 伝播 | する(危険) | しない |
基本方針は 迷ったら unknown。
any で型情報を捨てるより、unknown で受けて絞り込む方が TypeScript の恩恵を活かせます。
any を見かけたら「これは本当に any である必要があるか?」を一度考えるだけで、コードの安全性はかなり変わります。