TypeScriptの「インターフェース・メソッドオーバーロード」を完全に掌握する
こんにちは。日々TypeScriptの型システムと対峙していると、「なぜこの関数はこんなに呼び出しパターンがあるのか?」という設計に出会うことがありますよね。
今回は、TypeScriptにおけるメソッドオーバーロードについて深掘りしていきます。「ただ書くだけ」なら簡単ですが、その裏側にある「型評価の仕組み」と「実装の流儀」を理解すれば、あなたの書くコードの安全性は劇的に向上します。
ここをクリアすれば、TypeScriptの型システムの基本はもうバッチリ。一緒にマスターしていきましょう。
—
1. メソッドオーバーロードとは何か?(イメージ図)
メソッドオーバーロードを一言で言うと、「同じ名前の関数に、複数の異なる『入力と出力のルール』を与えること」です。
例えば、ユーザーのIDを受け取って詳細を返す関数があるとします。
- IDが「数値」なら、そのユーザーを1人返す。
- IDが「配列」なら、該当する複数のユーザーを返す。
これを一つの関数名で表現するために、TypeScriptでは「シグネチャ(型定義)」を重ねて記述します。
// 概念図:複数の入り口(シグネチャ)を一つの出口(実装)にまとめる
// [Input A] -> [実装] -> [Output A]
// [Input B] -> [実装] -> [Output B]
—
2. インターフェースでの定義方法
まずは、`interface` 内でオーバーロードを定義する基本形を見てみましょう。
interface UserFetcher {
// オーバーロード・シグネチャ(複数書ける)
fetchUser(id: number): User;
fetchUser(ids: number[]): User[];
}
ここで重要なのは、「シグネチャを分けることで、エディタ(VSCodeなど)が文脈に合わせて最適な型を推論してくれる」という点です。呼び出し元は、自分が渡した引数に応じて、戻り値が単体か配列かを即座に判断できます。
—
3. 実装の極意:実装シグネチャの罠
ここが初心者が最も躓きやすいポイントです。「インターフェースで定義したシグネチャ」と「実際に関数を実装する場所」の整合性です。
TypeScriptの仕様として、実装側は「すべてのオーバーロードを包含できる最も広い型」である必要があります。
class UserFetcherImpl implements UserFetcher {
// これが実装シグネチャ(外部からは呼び出せない「舞台裏」)
// すべての可能性(number | number[])を許容しなければならない
fetchUser(id: number | number[]): User | User[] {
if (typeof id === ‘number’) {
// ここでは id は number と確定(型ガードによる絞り込み)
return { id, name: “Alice” };
} else {
// ここでは id は number[] と確定
return id.map(i => ({ id: i, name: “Bob” }));
}
}
}
なぜ「実装シグネチャ」は外部から呼べないのか?
TypeScriptのコンパイラは、コンパイル時にオーバーロードのシグネチャをチェックし、実装部分は「中身のロジック」として扱います。もし実装シグネチャを個別に公開してしまうと、型安全性が崩壊する恐れがあるため、「最後に書いた関数定義は、外部からは見えない」というルールになっているのです。
—
4. 陥りやすい文法エラー
よくあるミスを整理しておきましょう。
1. 実装シグネチャが足りない:
各オーバーロードの引数や戻り値の型を網羅していないと、「実装がシグネチャと一致しません」というエラーが出ます。常に `union type` (A | B) で包み込む意識を持ちましょう。
2. パラメータの数が不一致:
オーバーロード間でパラメータの数が異なる場合、オプション引数(`?`)を使って、すべてを包含できるように調整する必要があります。
—
5. 実戦的な使い分け:Type Aliasとの違い
「`interface` じゃなくて `type` で良くない?」という質問をよく受けます。
- Interface: 拡張性が高く、オブジェクト指向的なクラス設計に向いている。宣言の結合(Declaration Merging)が可能。
- Type Alias: `|` や `&` を使った複雑な型結合に強い。
結論、「メソッド定義を含むクラスやインスタンスの構造を定義するなら Interface」、「純粋な関数のシグネチャを定義するなら Type」という使い分けが、モダンなTypeScript開発のベストプラクティスです。
—
最後に:TypeScriptが目指すもの
メソッドオーバーロードを学ぶことは、単なる文法の習得ではありません。「コードの利用者に、どのような型体験を提供したいか」を考える設計思想そのものです。
「この関数はどんな引数を受け取ると、どんな顔をして結果を返すのか?」
それを丁寧に定義してあげることで、あなたのコードはチームメンバーにとって「ドキュメントを読まなくても理解できる魔法の道具」になります。
今回のオーバーロードの考え方をマスターすれば、型定義の自由度が格段に広がります。ぜひ、実際のプロジェクトで意識的に使ってみてくださいね。
また次のステップでお会いしましょう。Happy Hacking!