mcpField
P
precise-any-variants

precise-any-variants

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

precise-any-variants について

precise-any-variants は、`any` を避けられない場合に、より限定的な型を選ぶための TypeScript ガイダンススキルです。型チェックを一部保つ方法と、`unknown` のほうが安全な場面を説明します。

主な機能

  1. 01`any[]` など具体的な型を推奨
  2. 02オブジェクト入力に `Record` を使用
  3. 03呼び出し可能な入力に関数型を使用
  4. 04より安全な `unknown` の使い方を解説
  5. 05TypeScript の例と注意点を提示

活用シーン

  1. 01動的データを受け取る関数の型付け
  2. 02JavaScript からのコード移行
  3. 03広すぎる `any` 注釈の見直し
  4. 04安全性の高い型での JSON 解析

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill precise-any-variants

インストール後、Claude Code で/precise-any-variantsを入力して読み込まれたか確認します。

詳細

単なる any ではなく、より具体的な any のバリエーションを優先する

概要

any を使わざるを得ない場合は、できるだけ具体的にしてください。

単なる any は何でも受け入れます。しかし、any[]、Record<string, any>、または () => any はより限定的であり、型チェックもある程度行われます。

このスキルを使う場面

  • 何らかの理由で any の使用を強いられる場合
  • 「何でも」受け入れる関数を書く場合
  • 真に動的なデータを扱う場合
  • JavaScript から移行する場合

絶対ルール

any is a last resort.
Specific variants of any preserve partial type safety.

覚えておいてください:

  • any[] は配列であることをチェックします
  • Record<string, any> はオブジェクトであることをチェックします
  • () => any は関数であることをチェックします
  • unknown は、どの any のバリエーションよりも安全です

検出:範囲が広すぎる any

function getLength(x: any) {  // Too broad!
  return x.length;
}

getLength(123);     // No error, crashes at runtime
getLength(null);    // No error, crashes at runtime
getLength([1,2,3]); // OK

より良い選択肢:具体的な any のバリエーション

配列には any[] を使う
function getLength(array: any[]) {
  return array.length;  // Type checked!
}

getLength([1, 2, 3]);  // OK
getLength(/regex/);
//        ~~~~~~~
// Argument of type 'RegExp' is not assignable to parameter of type 'any[]'

利点:

  • .length へのアクセスが型チェックされます
  • 戻り値の型は any ではなく number です
  • 配列以外は拒否されます
オブジェクトには Record<string, any> を使う
function hasKey(obj: Record<string, any>, key: string): boolean {
  return key in obj;
}

hasKey({ a: 1 }, 'a');  // OK
hasKey(null, 'a');
//     ~~~~
// Argument of type 'null' is not assignable to parameter of type 'Record<string, any>'
関数には () => any を使う
type Fn0 = () => any;           // No params
type Fn1 = (arg: any) => any;   // One param
type FnN = (...args: any[]) => any;  // Any params

function callTwice(fn: FnN) {
  fn();
  fn();
}

callTwice(() => console.log('hi'));  // OK
callTwice(123);
//        ~~~
// Argument of type 'number' is not assignable to parameter of type '(...args: any[]) => any'

可変長引数には any[] を使う

// any rest parameter: return type is any
const numArgsBad = (...args: any) => args.length;
//    ^? (...args: any) => any

// any[] rest parameter: return type is number
const numArgsBetter = (...args: any[]) => args.length;
//    ^? (...args: any[]) => number

戻り値の型は、後続のコードに影響します!

any のバリエーションの比較

型受け入れるもの拒否するもの
anyすべてなし
any[]配列配列以外
Record<string, any>オブジェクトプリミティブ型、null
() => any関数関数以外
objectオブジェクト、配列プリミティブ型、null
unknownすべてチェックなしではすべて

代わりに unknown を検討する

unknown はさらに安全です:

function process(data: unknown) {
  // Must narrow before using
  if (Array.isArray(data)) {
    data.length;  // OK, data is any[]
  }
  if (typeof data === 'object' && data !== null) {
    // data is object
  }
}

unknown を使うと、型をチェックするまでは何もできません。

実例:JSON の解析

// Don't return any
function parseBad(json: string): any {
  return JSON.parse(json);
}

// Better: return unknown
function parseGood(json: string): unknown {
  return JSON.parse(json);
}

// Caller must narrow:
const data = parseGood('{"x": 1}');
if (typeof data === 'object' && data !== null && 'x' in data) {
  console.log(data.x);
}

any が避けられない場合

本当に any が必要な場合もあります:

// Wrapping a library that uses any internally
function wrapLibrary<T>(input: T): T {
  return (library as any).process(input);
}

// Type assertion in implementation
function merge<T>(a: Partial<T>, b: Partial<T>): T {
  return { ...a, ...b } as any as T;
}

その場合でも、適切な型シグネチャを持つ関数の中に隠してください(項目 45)。

圧力に負けないためのプロトコル

1. 「any で動く」

圧力:「とにかく any を使って先に進もう」

回答: any は型チェックをすべて無効にします。具体的なバリエーションなら、ある程度の安全性を維持できます。

対応: 機能する範囲で最も具体的なバリエーションを使います。

2. 「型がわからない」

圧力:「型が本当に動的です」

回答: 型が本当に不明な場合は、unknown のほうが安全です。

対応: unknown を使い、使用する前に型を絞り込みます。

危険信号 — 立ち止まって再検討する

  • 関数の引数に any を使う(具体的なバリエーションを使う)
  • 戻り値の型に any を使う(unknown を優先する)
  • オブジェクトに any を使う(Record<string, any> または object を使う)
  • 配列に any を使う(any[] を使う)

よくある正当化(すべて誤り)

言い訳実態
「型付けするには動的すぎる」真に動的なデータには unknown を使えます
「具体的なバリエーションは冗長だ」数文字加えるだけで実行時エラーを防げます
「内部コードなら any で問題ない」内部コードにもバグはあります

クイックリファレンス

// DON'T: Plain any
function f(x: any): any { ... }

// DO: Specific variants
function getLength(arr: any[]): number { ... }
function getKeys(obj: Record<string, any>): string[] { ... }
function call(fn: (...args: any[]) => any): void { ... }

// BEST: unknown when possible
function process(data: unknown): void {
  if (Array.isArray(data)) { ... }
}

要点

any を使わざるを得ない場合は、具体的にしてください。

any[]、Record<string, any>、() => any は、柔軟性を保ちながら型チェックもある程度維持します。ただし、unknown のほうが安全かどうかも検討してください。

参考資料

Dan Vanderkam 著「Effective TypeScript」、項目 44「単なる any ではなく、より具体的な any のバリエーションを優先する」に基づいています。

コメント