avoid-unnecessary-type-params について
ジェネリック関数や型を見直す TypeScript 開発者向けのスキルです。不要な型パラメーターが複雑さを増し、推論を妨げ、型の関係を示さないまま安全性を誤認させる点を説明します。
主な機能
- 01一度しか使われない型パラメーターの特定
- 02型の関係を作るジェネリックの原則
- 03型の弱い入力に unknown を提案
- 04ジェネリック関数シグネチャの簡素化
- 05推論を改善するかどうかの確認
活用シーン
- 01ジェネリック API シグネチャのレビュー
- 02型推論がうまくいかない原因の調査
- 03コード内の不要なジェネリックの削減
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ marius-townhouse/ effective-typescript-skills --skill avoid-unnecessary-type-paramsインストール後、Claude Code で/avoid-unnecessary-type-paramsを入力して読み込まれたか確認します。
詳細
不要使いない型パラメーターを避ける
概要
「ジェネリクスの黄金律」では、型パラメーターは関数シグネチャに2回以上登場するべきだとされています。型パラメーターが1回しか登場しない場合、何も関連付けておらず、不要である可能性が高いです。不要な型パラメーターは、誤った型安全性を感じさせ、型推論の成功率を下げることがあります。
このスキルは、余分な型パラメーターを見つけて取り除き、TypeScriptがより効果的に推論できる、より簡潔で保守しやすいコードを書くのに役立ちます。
このスキルを使う場面
- ジェネリック関数や型を書くとき
- 型シグネチャの明確さをレビューするとき
- 型パラメーターが宣言以外で1回しか登場しないとき
- 「柔軟性」のためにジェネリクスを追加したくなったとき
- 型推論がうまくいかない原因を調査するとき
絶対のルール
型パラメーターは、関係性を確立するために2回以上登場する必要があります。型パラメーターが1回しか登場しない場合、本当に必要かどうかを慎重に見直してください。
検出
次のようなパターンに注意してください。
// RED FLAGS - Unnecessary type parameters
function parseYAML<T>(input: string): T; // Return-only generic
type Fn = <T>(x: T) => void; // T only used once
function third<A, B, C>(a: A, b: B, c: C): C; // A and B appear once
function getLength<T extends Lengthy>(x: T): number; // T only in parameter
黄金律
型パラメーターは型同士を関連付けるためのものです。型パラメーターが1回しか登場しない場合、何も関連付けていません。
// GOOD: T appears twice, relating input and output
function identity<T>(arg: T): T {
return arg;
}
// BAD: T only appears once (in return type)
function parseYAML<T>(input: string): T;
// Equivalent to: function parseYAML(input: string): any
// Usage shows the problem:
const data: User = parseYAML('...'); // Looks safe, but T can be anything
const bad: Banana = parseYAML('...'); // Also "valid" - no error!
戻り値のみに使うジェネリクス
戻り値のみに使うジェネリクスは、明示的な as を使わない型アサーションと同等なので危険です。
// BAD: Return-only generic
declare function parseYAML<T>(input: string): T;
const user: User = parseYAML('...'); // No error, but no validation either
// GOOD: Return unknown, force explicit assertion
declare function parseYAML(input: string): unknown;
const user = parseYAML('...') as User; // Explicit about the assertion
// Or use validation:
const user = parseYAML('...');
if (isUser(user)) { // Type guard
// user is User here
}
不要なジェネリクスをシンプルにする
// BEFORE: Unnecessary type parameters
function third<A, B, C>(a: A, b: B, c: C): C {
return c;
}
// AFTER: Only keep C which appears twice
function third<C>(a: unknown, b: unknown, c: C): C {
return c;
}
ジェネリクスが必要な場合
// GOOD: K appears twice, relating parameter to return type
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// K appears in: parameter type (key: K) and return type (T[K])
// This IS a good use of generics
クラスのジェネリクス
// GOOD: T appears multiple times, relating class properties
class Container<T> {
private items: T[] = [];
add(item: T): void { this.items.push(item); }
getAll(): T[] { return this.items; }
}
// BAD: T only used in one method
class Joiner<T extends string | number> {
join(els: T[]): string {
return els.map(String).join(',');
}
}
// Better: Move T to method or use concrete type
class Joiner {
join(els: (string | number)[]): string {
return els.map(String).join(',');
}
}
// Best: Just a function
function join(els: (string | number)[]): string {
return els.map(String).join(',');
}
圧力に負けないための手順
「柔軟な」ジェネリクスを追加するよう求められたときは、次の点を確認してください。
- 登場回数を数える:型パラメーターは2回以上登場しますか?
- 関係性を確認する:複数の値の型を関連付けていますか?
- unknownを検討する:
unknownでも同じように使えますか? - 型推論をテストする:ジェネリクスによって型推論は改善しますか、それとも悪化しますか?
注意すべきパターン
| アンチパターン | 問題点 |
|---|---|
function f<T>(): T | 戻り値のみに使用され、型安全性がありません |
type Fn = <T>(x: T) => void | 戻り値でTを使用しておらず、不要です |
| 1回しか使わない型に対するジェネリクス | 利点がないのに複雑さが増します |
Tが1回しか登場しない T extends X | 制約が役に立ちません |
よくある正当化
「APIの柔軟性が高まります」
実際のところ:不要なジェネリクスは、誤った安全性を感じさせます。実際の柔軟性は高めず、混乱を招くだけです。
「後で必要になるかもしれません」
実際のところ:先回りして追加するのではなく、必要になったときにジェネリクスを追加してください。後からいつでも追加できます。
「よりプロフェッショナルに見えます」
実際のところ:複雑で不要な型よりも、シンプルで正確な型のほうがプロフェッショナルです。
クイックリファレンス
| パターン | 判定 | 修正方法 |
|---|---|---|
function f<T>(x: T): T | ✓ 良い | Tが入力と出力を関連付けています |
function f<T>(x: string): T | ✗ 悪い | unknown または具体的な型を使います |
function f<T>(x: T): void | ✗ 悪い | unknown または any を使います |
class C<T> { method(x: T): T } | ✓ 良い | Tが複数箇所の使用を関連付けています |
class C<T> { method(x: T): void } | ✗ 悪い | Tをメソッドに移すか、削除します |
要点
ジェネリクスの第一のルールは「使わないこと」です。型パラメーター同士の関係性を確立するときだけ追加してください。型パラメーターが1回しか登場しない場合は削除しましょう。そうすることで、コードがより簡潔になり、型推論も改善します。
参考資料
- Dan Vanderkam著『Effective TypeScript, 2nd Edition』
- 項目51:不要な型パラメーターを避ける