unknown と any の使い分け
any と unknown はどちらも「何でも入る型」だが、その後の扱いがまったく異なる。any は型チェックを放棄し、unknown は型チェックを強制する。この違いを理解しておかないと、TypeScript を使う意味が半減してしまう。
any は型システムの非常口
any を使うと、その変数に対するすべての型チェックが無効になる。
const value: any = "hello";
// 何をしてもエラーにならない
console.log(value.foo.bar.baz);
console.log(value());
console.log(value[0]);これは便利なようで危険だ。実行時に TypeError: Cannot read property 'bar' of undefined のようなエラーが発生する可能性があるが、TypeScript はそれを検出できない。
any は型システムに「この変数のことは忘れてくれ」と伝えているようなものである。
unknown は安全な「何でも入る型」
unknown も任意の値を受け入れるが、そのまま使うことはできない。
const value: unknown = "hello";
// これらは全部エラーになる
// console.log(value.length);
// console.log(value());
// console.log(value[0]);
// 型を絞り込めば使える
if (typeof value === "string") {
console.log(value.length); // OK
}unknown は「何が入っているかわからないから、確認してから使え」という意味だ。
何でも入る。何でもできる。型チェックなし。危険。
何でも入る。そのままでは何もできない。型チェックを強制。安全。
実際の使い分け
外部から入ってくるデータを扱うとき、unknown が活躍する。
// API レスポンスを受け取る
async function fetchUser(): Promise<unknown> {
const res = await fetch("/api/user");
return res.json();
}
// 使う側で型を検証する
const data = await fetchUser();
if (isUser(data)) {
console.log(data.name);
}
function isUser(value: unknown): value is User {
return (
typeof value === "object" &&
value !== null &&
"name" in value &&
typeof (value as User).name === "string"
);
}any を使うと型チェックをすり抜けてしまうが、unknown なら型ガードを書かざるを得ない。この「強制力」が安全性を担保する。
any を使ってもいい場面
とはいえ、any が完全に悪というわけではない。
JavaScript から TypeScript への移行中、すべてに型を付けるのは現実的でないことがある。一時的に any を使い、徐々に型を付けていく戦略は有効。
サードパーティライブラリの型定義が不完全だったり、型パズルが複雑になりすぎる場合、any で逃げるのも選択肢。ただし // eslint-disable-next-line @typescript-eslint/no-explicit-any のようなコメントで意図を明示すべき。
unknown を使うべき場面
特に catch ブロックは注意が必要だ。TypeScript 4.4 以降、catch の引数はデフォルトで unknown になった。
try {
throw new Error("something went wrong");
} catch (e) {
// e は unknown 型
if (e instanceof Error) {
console.log(e.message);
}
}結論として、新規コードでは any より unknown を優先すべきだ。型の絞り込みを強制されることで、実行時エラーを未然に防げる。any は最後の手段として温存しておこう。