mcpField
P
prefer-unknown-over-any

prefer-unknown-over-any

github.com/marius-townhouse開発658 回インストール39 週間前に更新
リポジトリ

prefer-unknown-over-any について

値の型が不明な場合に `any` ではなく `unknown` を使うよう勧める、TypeScript 開発者向けのコーディングスキルです。外部データや解析結果、汎用的な関数引数を例に、型の絞り込みと検証方法を説明します。

主な機能

  1. 01型不明の値には `unknown` を優先
  2. 02利用前に型を絞り込む
  3. 03JSON、YAML、API データの検証
  4. 04型ガードと型アサーションの活用

活用シーン

  1. 01JSON や YAML の安全な解析
  2. 02外部データやユーザー入力の処理
  3. 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 の違い

特性anyunknown
あらゆる型を代入できるはいはい
あらゆる型に代入できるはいいいえ
メソッドを呼び出せるはい(危険!)いいえ(安全!)
使用前に型を絞り込む必要があるいいえはい

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 を使う」に基づいています。

コメント