Menu

TypeScriptのanyとunknownの違いと使い分け

any と unknown はどちらもあらゆる値を受け付けます。any はその値の型チェックを止め、unknown は使う前に値をチェックさせます。違い、unknown の絞り込み方、noImplicitAny、型付きのコードに any が紛れ込む場所を解説します。

このページのコードはエディタで実行できます - 編集してすぐに結果を確認できます。

any と unknown はどちらもあらゆる値を受け付けます。違いは、そのあと値に対して何ができるかです。any は何でもでき、何もチェックしません。unknown は、値が何であるかを証明するまでほとんど何もできません。

@ts-expect-error の行がなければ、u.toUpperCase() はコンパイルエラー TS18046 です。typeof のチェックが u を string に絞り込み、そのブロックの中では文字列のメソッドがすべて使えます。

any と unknown の比較

anyunknown
あらゆる値を受け付けるかはいはい
string や number などに代入できるかはい、チェックなしいいえ(TS2322)
プロパティを読む、メソッドを呼ぶはい、チェックなしいいえ(TS18046)
関数として呼ぶはいいいえ
算術演算と比較(x * 2、x + 1、x < 5)はいいいえ(TS18046)
使う前にチェックが必要かいいえはい(typeof、instanceof、in、型ガード)
型チェックへの影響その値とそれが触れるものすべてで止まる保たれる

型理論の言葉で言えば、unknown はトップ型です。あらゆる型を代入でき、unknown 自身は unknown と any にしか代入できません。any は抜け道で、never 以外のすべてに対して双方向に代入できます。

any は型チェックを止める

any 型の値は無条件に信用されます。コンパイラーは打ち間違い、間違った型、存在しないプロパティを受け入れ、間違いは実行時に表れます。

出力が問題を示しています。number と型注釈を付けた変数が文字列を持ち、最後の行は Cannot read properties of undefined (reading 'city') を投げます。any は広がりもします。user.name は any なので、そこから計算した値もすべて any になり、型のない1つの値が、入ってきた場所から遠く離れたところでチェックを止めてしまいます。

unknown は先にチェックさせる

unknown では、コードが値を絞り込むまで、コンパイラーはあらゆる操作を拒否します。絞り込みには普通のJavaScriptのチェックを使い、各ブランチの中では値はチェックした型になります。

typeof null は "object" なので、value === null のチェックはオブジェクトのチェックより前に置く必要があります。"id" in value のあとでは、オブジェクトが id プロパティを持つことをTypeScriptは知っていますが、中身については何もわからないので型は unknown です。String() で明示的にテキストに変換しています。数値として使うなら、先に typeof でチェックします。

アサーション value as string でチェックを省くこともでき、コンパイラーはそれを受け入れます。しかしそれは実行時のチェックの裏付けのない約束なので、本当のチェックを選びましょう。型アサーション を参照してください。

unknown でJSONを検証する

JSON.parse は any を返すと宣言されているので、その結果は黙ってチェックを無効にします。結果に unknown と型注釈を付け、残りのコードが信用する前に形をチェックする型ガードを書きましょう。

最初の入力は dark at 14px を出力し、2つ目は fontSize がないので拒否されます。any なら、2つ目の入力は fontSize が undefined の Settings としてそのまま流れてしまいます。大きなスキーマなら、検証ライブラリが同じことをして型も作ってくれます。

noImplicitAny

any は、誰かが書いた場合にだけ現れるわけではありません。型注釈がなく推論の手がかりもない引数も any になります。strict で有効になる noImplicitAny オプションは、それをエラーとして報告します:

index.ts(2,17): error TS7006: Parameter 'x' implicitly has an 'any' type.

型注釈を付ければ直ります: function double(x: number)。明示的に x: any と書いてもコンパイルでき、それが狙いです。any がコードの中で目に見える形で残り、検索もレビューもできます。

それでも any が紛れ込む場所

strict のもとでも、次のものはコードに any という単語が現れないまま any を生みます:

発生源得られる型対処法
JSON.parse(text)anyunknown と型注釈を付けて検証する
ブラウザー(DOM)の型での response.json()Promise<any>JSON.parse と同じ(Node 自身の fetch の型はすでに Promise<unknown> を返す)
型定義のないパッケージインポートが any(宣言を追加しない限りエラーも出る)@types/... をインストールするか .d.ts を書く
value as anyany型ガードか正確なアサーションを使う
useUnknownInCatchVariables が無効な catch (e)anystrict なら unknown になる。そのままにしておく

何でも throw でき、Error オブジェクトだけとは限らないので、strict のもとでは catch の変数は unknown です。e.message を読む前に e instanceof Error で絞り込みましょう。

any を使ってもよい場面

any は禁止されているわけではありませんが、使うたびにコンパイラーが守らない場所が1つ増えます。妥当な使い方は次のとおりです:

  • JavaScriptのコードベースを移行している最中で、まだ型付けされていない部分の目印として。
  • 型システムでうまく表現できないコードで、小さく保ち、型付きの関数シグネチャの内側に閉じ込めたもの。
  • 意図的に不正なデータを渡すテストコード。

それ以外はすべて、unknown がチェックを保ったまま同じ「この型はわからない」というケースをカバーします。多くのチームは lint ルール @typescript-eslint/no-explicit-any でこれを強制しています。関連する型 Record<string, unknown> は、「値のわからない何かのオブジェクト」を表すときの定番です。

よくある質問

TypeScriptの any と unknown の違いは何ですか?

どちらもあらゆる値を受け付けます。any なら値に何でもでき(プロパティを読む、呼び出す、number に代入する)、コンパイラーは何もチェックしません。unknown では、typeof x === "string" のようなチェックで絞り込むまでほとんど何もできません。unknown は型チェックを保つので、より安全な選択です。

any ではなく unknown を使うべきなのはどんなときですか?

コンパイル時に値の型がわからないときはいつでもです。パースしたJSON、ネットワークのレスポンスのデータ、捕捉したエラー、検証関数の入力などです。unknown と型付けして絞り込みましょう。any は、短期的な移行作業や型システムで表現できないコードにだけ使います。

「Object is of type 'unknown'」とはどういう意味ですか?

エラー TS18046('x' is of type 'unknown')と TS2571(Object is of type 'unknown'、load().id のように値が単純な名前でないときに使われます)は、unknown の値を特定の型があるかのように使った、たとえばプロパティを読んだりメソッドを呼んだりしたという意味です。先に型をチェックし(typeof、instanceof、Array.isArray、in、型ガード関数)、絞り込んだブランチの中で値を使いましょう。

JSON.parse が any を返すのはなぜですか?

文字列に何が入っているかをコンパイラーは知りようがないので、標準ライブラリでの宣言が parse(text: string, ...): any になっているからです。その結果は、触れるものすべてのチェックを黙って止めてしまいます。代わりに型注釈を付けましょう: const data: unknown = JSON.parse(text) とし、使う前に検証します。

noImplicitAny とは何ですか?

strict に含まれるコンパイラーのオプションで、型注釈がなく推論の手がかりもないために宣言が黙って any 型になってしまう場合にエラーを報告します。引数なら TS7006、型を割り出せない変数なら TS7005 か TS7034 です。誰も書いていないのに any が現れるのを防ぎます。明示的な any は引き続き使えます。

Coddy programming languages illustration

Coddyでコードを学ぼう

始める