narrow-any-scope について
`any` の使用が避けられない場合に、その影響を必要最小限に抑える TypeScript 開発者向けのコーディングスキルです。型定義のない依存コードや複雑な型エラーへの対処、コードレビューに役立ちます。
主な機能
- 01代入時に `any` の範囲を限定
- 02インラインの `any` アサーション
- 03関数の戻り値型を注釈
- 04型付き変数で値を制約
- 05精密なコールバック型を使用
活用シーン
- 01型定義のない外部コードへの対応
- 02JavaScript から TypeScript への移行
- 03`any` を使うコードのレビュー
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill narrow-any-scopeインストール後、Claude Code で/narrow-any-scopeを入力して読み込まれたか確認します。
詳細
あらゆる型で可能な限り狭いスコープを使う
概要
anyを使わざるを得ない場合は、被害を封じ込めましょう。
anyには伝染性があり、コード全体に広がります。型安全性への影響を抑えるため、可能な限り狭いスコープに限定してください。
このスキルを使う場面
- 何らかの理由でanyを使わざるを得ない場合
- 型定義のないサードパーティコードを扱う場合
- 複雑な型エラーに対処する場合
- JavaScriptからTypeScriptへ移行する場合
- anyを使っているコードをレビューする場合
絶対ルール
NEVER let any escape its minimal necessary scope.
例外はありません:
- 「変数1つだけだから」
- 「後で直すから」
- 「関数が短いから」
検出:「anyが漏れ出している」兆候
anyをもっと狭いスコープにできるなら、そうしてください。
// ❌ VIOLATION: any on parameter escapes to return value
function processExpression(expression: any) {
const result = expression.evaluate(); // result is any
return result; // Returns any - spreads everywhere
}
// ✅ BETTER: any only where needed
function processExpression(expression: any) {
const result: number = expression.evaluate(); // Constrain immediately
return result; // Returns number
}
テクニック1:代入時に範囲を狭める
// ❌ BAD: any spreads
const config = getConfig() as any;
config.debug = true; // config stays any
const debug = config.debug; // debug is any
// ✅ GOOD: Constrain immediately
const config = getConfig() as any;
const debug: boolean = config.debug; // debug is boolean
テクニック2:anyの型アサーションをインラインで使う
// ❌ BAD: any on the whole object
function processObject(obj: any) {
obj.x = 1;
obj.y = 2;
return obj; // Returns any
}
// ✅ GOOD: any only on the problematic line
function processObject(obj: Foo) {
// @ts-expect-error - The library typings are wrong here
(obj as any).secretMethod();
return obj; // Returns Foo
}
テクニック3:戻り値の型注釈
// ❌ BAD: Return type infected by any
function parse(input: string) {
const parsed = JSON.parse(input); // parsed is any
return parsed; // Return type is any
}
// ✅ GOOD: Explicit return type blocks the any
function parse(input: string): ParsedData {
const parsed = JSON.parse(input); // Still any internally
return parsed; // But return type is ParsedData
}
テクニック4:中間変数
// ❌ BAD: any infects the whole expression
function calculate(a: number, b: any, c: number) {
return a + b + c; // Result is any because b is any
}
// ✅ BETTER: Convert any before use
function calculate(a: number, b: any, c: number) {
const bNum: number = b; // Constrain b to number
return a + bNum + c; // Result is number
}
テクニック5:any型の引数ではなく型アサーションを使う
// ❌ BAD: Changing parameter type to any
function processUser(user: any) {
return user.name.toUpperCase();
}
// ✅ GOOD: Keep the type, assert where needed
function processUser(user: User) {
// Only use any assertion if absolutely necessary
return (user as any).secretField; // any is inline only
}
any[]の落とし穴
// ❌ DANGEROUS: any[] elements are any
function getFirst(arr: any[]) {
return arr[0]; // Returns any
}
// ✅ SAFER: Use generics or unknown
function getFirst<T>(arr: T[]): T {
return arr[0]; // Returns T
}
function getFirst(arr: unknown[]): unknown {
return arr[0]; // Returns unknown - forces checking
}
関数シグネチャ:より正確なany
// ❌ BAD: Maximally broad any
type Callback = any;
// ✅ BETTER: Precise function types
type Callback0 = () => any; // No params
type Callback1 = (arg: any) => any; // One param
type CallbackN = (...args: any[]) => any; // Any params
プレッシャーに負けないためのプロトコル
1. 「この場所だけだから」
プレッシャー:「anyはこの箇所だけにとどまっています」
**回答:**anyは戻り値の型、代入、関数呼び出しを通じて広がります。
**対処:**スコープを狭めてください。すぐに型の制約を追加してください。
2. 「型が複雑すぎる」
プレッシャー:「適切な型がわかりません」
**回答:**一時的にanyを使ってもかまいませんが、出力には制約を設けてください。
**対処:**明示的な戻り値の型を追加してください。中間変数にも型注釈を追加してください。
3. 「内部コードだから」
プレッシャー:「このコードを使う外部コードはありません」
**回答:**将来のあなたにとって、今のあなたのコードは外部コードです。
**対処:**anyのスコープを狭めてください。なぜ必要なのかを記録してください。
危険信号 — 作業を止めて再検討する
- 関数の引数にanyを使っている
- anyを返す関数(特に戻り値の型が明示されていない場合)
- 複数回使われる変数にanyを代入している
- 式を通じてanyが広がっている
- 複数の関数間でanyを渡している
よくある自己正当化(すべて誤り)
| 言い訳 | 実際のところ |
|---|---|
| 「封じ込めている」 | anyは戻り値や代入を通じて広がります。 |
| 「1回しか使わない」 | それはバグが起こるのを待っているようなものです。 |
| 「後で直す」 | その「後」は決して来ません。今、スコープを狭めてください。 |
| 「関数が短い」 | 短い関数でもanyを広げることがあります。 |
クイックリファレンス
| anyの状況 | 範囲を狭めるテクニック |
|---|---|
| any型の引数 | 戻り値の型注釈を追加する |
| any型の戻り値 | 明示的な戻り値の型を追加する |
| 式の中のany | 型付きの変数に代入する |
| JSON.parse由来のany | 戻り値の型を追加するか、検証する |
| ライブラリ由来のany | 具体的な型にアサーションする |
anyの使用状況を監査する
// Before: any everywhere
function process(data: any): any {
const x = data.foo; // any
const y = data.bar; // any
return { x, y }; // any
}
// After: any contained
function process(data: any): Result {
const x: number = data.foo; // number
const y: string = data.bar; // string
return { x, y }; // Result
}
要点
anyは危険物として扱ってください。漏出を封じ込めましょう。
anyを使わざるを得ない場合は、可能な限り小さなコード片だけで使ってください。型注釈を追加して、広がるのを防ぎましょう。anyが必要な1行を除き、すべての場所で型安全性を保つことが目標です。
参考資料
Dan Vanderkam著「Effective TypeScript」、項目43「Use the Narrowest Possible Scope for any Types」に基づいています。