prefer-unknown-over-any について
値の型が不明な場合に `any` ではなく `unknown` を使うよう勧める、TypeScript 開発者向けのコーディングスキルです。外部データや解析結果、汎用的な関数引数を例に、型の絞り込みと検証方法を説明します。
主な機能
- 01型不明の値には `unknown` を優先
- 02利用前に型を絞り込む
- 03JSON、YAML、API データの検証
- 04型ガードと型アサーションの活用
活用シーン
- 01JSON や YAML の安全な解析
- 02外部データやユーザー入力の処理
- 03危険な `any` 型の置き換え
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill prefer-unknown-over-anyインストール後、Claude Code で/prefer-unknown-over-anyを入力して読み込まれたか確認します。
詳細
型が不明な値には any ではなく unknown を使う
概要
unknown は any の型安全な親戚です。
値の型がわからない場合は、unknown を使います。使用する前に型を絞り込む必要があるため、any なら見逃してしまうバグを防げます。
このスキルを使う場面
- JSON や YAML を解析するとき
- 外部ソースからデータを受け取るとき
- ユーザー入力を扱うとき
- あらゆる値を受け取る関数を書くとき
- any をより安全な型に置き換えるとき
厳守すべきルール
NEVER use any when unknown would work.
例外はありません:
- 「unknown は冗長だから」という理由で使わないこと
- 「安全だとわかっているから」という理由で使わないこと
- 「実行時にもチェックするから」という理由で使わないこと
見分け方:「型不明のデータに any を使う」というにおい
型がわからないために any を使っているなら、代わりに unknown を使います。
// ❌ VIOLATION: any allows dangerous operations
function parseYAML(yaml: string): any {
// ...
}
const data = parseYAML('...');
data.foo.bar.baz(); // No error! But will crash
data('call me maybe'); // No error! But will crash
// ✅ CORRECT: unknown forces you to check first
function safeParseYAML(yaml: string): unknown {
// ...
}
const data = safeParseYAML('...');
data.foo.bar.baz(); // Error: 'data' is of type 'unknown'
data('call me maybe'); // Error: 'data' is of type 'unknown'
any と unknown の違い
| 特性 | any | unknown |
|---|---|---|
| あらゆる型を代入できる | はい | はい |
| あらゆる型に代入できる | はい | いいえ |
| メソッドを呼び出せる | はい(危険!) | いいえ(安全!) |
| 使用前に型を絞り込む必要がある | いいえ | はい |
unknown を安全に使う
型アサーションを使う場合(確信があるとき)
interface Book {
name: string;
author: string;
}
const data = safeParseYAML(`
name: Wuthering Heights
author: Emily Bronte
`) as Book; // You assert it's a Book
console.log(data.name); // OK
instanceof を使う場合(クラスの場合)
function processValue(value: unknown) {
if (value instanceof Date) {
value.toISOString(); // OK - value is Date
}
}
typeof を使う場合(プリミティブの場合)
function processValue(value: unknown) {
if (typeof value === 'string') {
value.toUpperCase(); // OK - value is string
}
if (typeof value === 'number') {
value.toFixed(2); // OK - value is number
}
}
型ガードを使う場合(独自のチェックの場合)
interface Book {
name: string;
author: string;
}
function isBook(value: unknown): value is Book {
return (
typeof value === 'object' &&
value !== null &&
'name' in value &&
'author' in value
);
}
function processValue(value: unknown) {
if (isBook(value)) {
console.log(value.name, value.author); // OK - value is Book
}
}
よくある使用例
JSON の解析
// ❌ BAD: JSON.parse returns any by default
const data = JSON.parse(response);
data.anything.goes(); // No error, but might crash
// ✅ GOOD: Parse as unknown, then validate
function parseJSON(text: string): unknown {
return JSON.parse(text);
}
const data = parseJSON(response);
if (isValidResponse(data)) {
// Now data is the validated type
}
API レスポンス
// ❌ BAD: Trusting the response
async function fetchUser(id: string): Promise<any> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
// ✅ GOOD: Type unknown, validate before use
async function fetchUser(id: string): Promise<unknown> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
const user = await fetchUser('123');
if (isUser(user)) {
console.log(user.name); // Safe!
}
「あらゆる値を受け取る」汎用関数
// ❌ BAD: any loses all type info
function isSmallArray(arr: any[]): boolean {
return arr.length < 10;
}
// ✅ GOOD: unknown[] is safer
function isSmallArray(arr: readonly unknown[]): boolean {
return arr.length < 10;
}
unknown の型を絞り込む
TypeScript では、unknown を使う前にその型を証明する必要があります。
function process(value: unknown) {
// Direct use: Error
value.toString(); // Error: 'value' is of type 'unknown'
// After null check
if (value !== null && value !== undefined) {
// Still unknown, but non-nullish
}
// After typeof check
if (typeof value === 'string') {
value.toUpperCase(); // OK: value is string
}
// After instanceof check
if (value instanceof Date) {
value.toISOString(); // OK: value is Date
}
// After type guard
if (isBook(value)) {
value.author; // OK: value is Book
}
}
object、{}、unknown
| 型 | 含まれるもの | 含まれないもの |
|---|---|---|
unknown | すべて | 除外されるものはありません |
{} | null/undefined 以外のすべて | null、undefined |
object | オブジェクト、配列、関数 | プリミティブ、null、undefined |
// unknown is most broad - prefer it for "I don't know"
function acceptAnything(value: unknown) {}
// {} excludes null/undefined
function acceptNonNullish(value: {}) {}
// object excludes primitives
function acceptObject(value: object) {}
圧力に負けないための指針
1. 「unknown は冗長すぎる」
圧力:「あちこちにチェックを追加しなければなりません」
回答: そのチェックがバグを見つけます。any ではバグを見逃してしまいます。
対応: チェックを書きましょう。チェックはドキュメントにもなります。
2. 「実行時の型はわかっている」
圧力:「使う前に型をチェックしています」
回答: unknown を使えば、TypeScript がチェックの正しさを検証します。
対応: unknown を使い、型の絞り込みを TypeScript に追跡させましょう。
3. 「JSON.parse は any を返す」
圧力:「組み込み関数は any を返します」
回答: unknown を返す関数でラップしましょう。
対応: unknown を返す safeParseJSON ヘルパーを作成しましょう。
危険信号 — STOP して考え直す
- 外部ソースからの値に
anyを使っている - 解析した JSON/YAML に
anyを使っている - API レスポンスの本文に
anyを使っている - 「型がわからない」という理由だけで
anyを使っている unknownを受け取れるのに、anyを受け取る関数を書いている
よくある正当化(すべて誤り)
| 言い訳 | 実際のところ |
|---|---|
| 「実行時にチェックしています」 | unknown を使えば、TypeScript がチェックを検証します。 |
| 「冗長すぎます」 | 冗長さがバグを見つけます。 |
| 「any のほうが簡単です」 | 書くのは簡単でも、デバッグは難しくなります。 |
| 「ソースを信頼しています」 | 外部ソースは決して信頼できません。 |
クイックリファレンス
| 状況 | 使う型 |
|---|---|
| 型がわからない | unknown |
| JSON/YAML を解析する | unknown を返し、その後検証する |
| 外部 API のデータ | 検証するまでは unknown |
| あらゆる値を受け取る | unknown(any ではない) |
| あらゆる値の配列 | unknown[](any[] ではない) |
要点
unknown は「この型はわかりません」という意味です。any は「型を気にしません」という意味です。
値の型が本当にわからないときは unknown を使いましょう。使用前に型を絞り込む必要があるため、any では見逃してしまうバグを見つけられます。コードがより安全になり、型情報の出どころも明確になります。
参考資料
Dan Vanderkam 著『Effective TypeScript』の項目 46「型が不明な値には any ではなく unknown を使う」に基づいています。