precise-any-variants について
precise-any-variants は、`any` を避けられない場合に、より限定的な型を選ぶための TypeScript ガイダンススキルです。型チェックを一部保つ方法と、`unknown` のほうが安全な場面を説明します。
主な機能
- 01`any[]` など具体的な型を推奨
- 02オブジェクト入力に `Record` を使用
- 03呼び出し可能な入力に関数型を使用
- 04より安全な `unknown` の使い方を解説
- 05TypeScript の例と注意点を提示
活用シーン
- 01動的データを受け取る関数の型付け
- 02JavaScript からのコード移行
- 03広すぎる `any` 注釈の見直し
- 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 のバリエーションを優先する」に基づいています。