mcpField
C
conditional-types-over-overloads

conditional-types-over-overloads

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

conditional-types-over-overloads について

関連する入力に応じて戻り値が変わる場合に、オーバーロードより条件型を優先するための TypeScript スキルです。ユニオンへの分配、実装方法、個別のオーバーロードが適切な場面を説明します。

主な機能

  1. 01関連する戻り値の条件型表現
  2. 02ユニオン入力への対応
  3. 03オーバーロードの比較
  4. 04実装方法の指針

活用シーン

  1. 01正確なジェネリック関数シグネチャの設計
  2. 02追加のオーバーロードなしでユニオン入力に対応
  3. 03オーバーロードが多い宣言の見直し

インストール

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

$npx skills add https://github.com/marius-townhouse/effective-typescript-skills --skill conditional-types-over-overloads

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

詳細

オーバーロードシグネチャより条件型を優先する

概要

関数の戻り値の型が入力の型に依存する場合、オーバーロードシグネチャを使いたくなるかもしれません。しかし、多くの場合、条件型のほうが優れた解決策になります。オーバーロードは個別にチェックされますが、条件型はユニオン型に対して分配され、単一の式として解析できます。そのため、ユニオン型の入力をより柔軟に扱え、保守しやすい型宣言を実現できます。

条件型とオーバーロードを使い分ける方法を理解することは、柔軟で正確な型シグネチャを書くうえで重要です。

このスキルを使う場面

  • 関数の戻り値の型が入力の型に依存する
  • 関数がユニオン型を受け取る
  • 複数のオーバーロードシグネチャを検討している
  • 入力に応じて正確な戻り値の型が必要
  • 型を変換する型ユーティリティを構築している

絶対原則

オーバーロードシグネチャより条件型を優先してください。条件型はユニオン型に対して分配され、より正確で保守しやすい型宣言を実現します。

検出

次のようなパターンに注意してください。

// RED FLAGS - Overloads that should be conditionals
declare function process(x: string): string;
declare function process(x: number): number;
declare function process(x: boolean): boolean;
// Missing union case!

// Or: Multiple overloads for what should be one conditional type
declare function transform(input: A): X;
declare function transform(input: B): Y;
declare function transform(input: C): Z;

オーバーロードの問題点

オーバーロードは個別にチェックされ、ユニオン型をうまく扱えません。

// Overload approach
declare function double(x: number): number;
declare function double(x: string): string;

// Works for individual types
const n = double(12);  // number
const s = double('x'); // string

// FAILS for unions
function f(x: string | number) {
  return double(x);
  //     ~~~~~~~~~
  // Error: No overload matches this call
}

条件型による解決策

条件型はユニオン型に対して自動的に分配されます。

// Conditional type approach
declare function double<T extends string | number>(
  x: T
): T extends string ? string : number;

// Works for individual types
const n = double(12);  // number
const s = double('x'); // string

// ALSO works for unions!
function f(x: string | number) {
  return double(x);  // string | number - correct!
}

分配の仕組み

T が string | number の場合、TypeScript は次のように評価します。

(string | number) extends string ? string : number
→ (string extends string ? string : number) |
  (number extends string ? string : number)
→ string | number

実例

// Event handler with different payloads
type EventMap = {
  click: { x: number; y: number };
  keypress: { key: string; code: string };
  load: { timestamp: number };
};

// BAD: Multiple overloads needed
declare function onEvent(
  type: 'click',
  handler: (payload: { x: number; y: number }) => void
): void;
declare function onEvent(
  type: 'keypress',
  handler: (payload: { key: string; code: string }) => void
): void;
// ... more overloads for each event type

// GOOD: Single conditional type
declare function onEvent<T extends keyof EventMap>(
  type: T,
  handler: (payload: EventMap[T]) => void
): void;

// Usage
onEvent('click', (e) => {
  console.log(e.x, e.y);  // e is { x: number; y: number }
});

onEvent('keypress', (e) => {
  console.log(e.key);  // e is { key: string; code: string }
});

実装方針

条件型の戻り値を持つ関数を実装する場合は、単一のオーバーロードを使用してください。

// External signature with conditional type
declare function double<T extends string | number>(
  x: T
): T extends string ? string : number;

// Implementation with simpler type
function double(x: string | number): string | number {
  return typeof x === 'string' ? x + x : x + x;
}

オーバーロードが適切な場合

次のような場合は、オーバーロードのほうがわかりやすいことがあります。

  • 関数が完全に異なる 2 つの関数として動作する
  • ユニオン型のケースが現実的にありそうにない
  • パラメーターの数や形状が異なる
// Node's readFile - distinct use cases
// Using callbacks vs Promises are truly different patterns
readFile(path, callback);      // callback version
readFile(path, options);       // Promise version
// Better as separate functions or clear overloads

プレッシャーに対抗するための手順

簡潔さを理由にオーバーロードを使うよう求められた場合は、次の点を確認してください。

  1. ユニオン型のケースをテストする:この関数がユニオン型の入力を受け取ることはありますか?
  2. 分配について検討する:型をユニオン型に対して分配させたいですか?
  3. 保守性を確認する:後からオーバーロードを追加する必要がありますか?
  4. 単一のオーバーロードを使う:実装では、内部でよりシンプルな型を使います

注意すべき点

アンチパターン問題点
関連する型に対する多数のオーバーロード保守が難しく、ユニオン型のケースを見落とします
分配されるべきオーバーロードユニオン型の入力で型チェックに失敗します
コピー&ペーストしたオーバーロードDRY 原則に反します
ユニオン型のオーバーロードがない利用者に型アサーションの使用を強います

よくある正当化

「オーバーロードのほうが読みやすい」

実際には:単一の条件型のほうが、5 個以上のオーバーロードよりシンプルなことがよくあります。複雑さの原因は条件式ではなく、シグネチャの数です。

「ユニオン型を扱う必要はありません」

実際には:TypeScript ではユニオン型がよく使われます。利用者が関数にユニオン型を渡すと、オーバーロードでは対応できません。

「必要になったらユニオン型のオーバーロードを追加します」

実際には:条件型はユニオン型を自動的に処理します。ユニオン型のオーバーロードを手動で追加すると、間違いが起きやすく、記述も冗長になります。

クイックリファレンス

アプローチユニオン型への対応保守性使用する場面
オーバーロードいいえ(手動)低い(多数のシグネチャ)本当に異なる関数
条件型はい(自動)高い(単一のシグネチャ)関連する戻り値の型
ユニオン型の戻り値はいシンプル正確さが不要な場合

要点

条件型はユニオン型に対して分配され、オーバーロードより正確で保守しやすい型宣言を実現します。関数が本当に異なる操作を表す場合に限り、オーバーロードを使用してください。

参考資料

  • Dan Vanderkam 著『Effective TypeScript』第 2 版
  • 項目 52:オーバーロードシグネチャより条件型を優先する

コメント