Interfaceのメソッド定義 vs 型エイリアス:TypeScriptの「型」を武器にする設計術
フロントエンドの設計において、`interface`でメソッドを定義すべきか、それとも型エイリアス(`type`)で関数型を定義すべきか。この問いに「どっちでもいい」と答えるエンジニアは、TypeScriptの型システムが持つ「計算資源としての側面」を理解できていません。
今日は、コンパイラの挙動から逆算した、堅牢でスケーラブルな型設計の境界線を伝授します。
—
1. 脳内コンパイラを起動せよ:なぜ `interface` メソッドは「危険」なのか
多くの開発者がやりがちな、オブジェクト指向を模した記述を見てみましょう。
// 悪い例:Interface内でのメソッド定義
interface UserActions {
updateName(name: string): void;
deleteUser(): Promise
}
この定義には、「型システムの非対称性」という致命的な落とし穴があります。
- `this` の束縛問題: `interface` 内のメソッドは、呼び出し側で `this` のコンテキストが崩れやすい。特にコールバックとして渡した瞬間に `this` が消失し、実行時エラー(`Cannot read property ‘…’ of undefined`)を誘発します。
- マージの罠: `interface` は宣言の結合(Declaration Merging)を許容します。意図せず他モジュールからメソッドが拡張されるリスクがあり、純粋なデータ構造を保持したい場合に邪魔になります。
2. 関数型アプローチ:データと振る舞いの分離
型エイリアスと「独立した関数」を組み合わせる設計こそが、現代のTypeScriptにおけるベストプラクティスです。
// 推奨:データと関数を分離する
export type User = {
id: string;
name: string;
};
// 関数型プログラミング的アプローチ
export const updateUserName = (user: User, newName: string): User => ({
…user,
name: newName,
});
なぜこの設計が「最強」なのか?
1. 純粋関数(Pure Functions)の恩恵: 状態(State)を変化させず、新しい値を返すため、Reactのレンダリング最適化や状態管理(Redux, Zustand等)との相性が抜群です。
2. Tree-shakingの最適化: オブジェクトのプロパティとしてメソッドを保持すると、未使用のメソッドであってもバンドルから除外できない場合があります。関数として独立していれば、必要なものだけをインポートするだけで済みます。
3. テストの容易性: クラスのインスタンスを生成して依存関係をモックする必要はありません。単なる引数として値を渡すだけで、100%のカバレッジが担保可能です。
—
3. 実践:非同期API連携における堅牢なパターン
実務でよくある「APIレスポンスからビジネスロジックを分離する」ケースで、具体的な設計例を示します。
// APIレスポンス型(純粋なデータ構造)
export type ApiResponse = {
readonly id: string;
readonly status: ‘active’ | ‘inactive’;
};
// サービス層:型エイリアスを活用したユーティリティ関数
export const isActive = (res: ApiResponse): boolean => res.status === ‘active’;
// コンポーネント設計での活用例
const UserProfile = ({ data }: { data: ApiResponse }) => {
// テンプレート内でメソッドを呼ぶのではなく、ロジックを分離
if (!isActive(data)) return
return
;
};
ここがポイント
- `readonly` を付与することで、不変性を型レベルで強制。
- `isActive` を関数として定義することで、コンポーネント側からは「データがどうなっているか」ではなく「どう判断するか」というロジックを読み取れる。
—
結論:どちらを選ぶべきか?
私の判断基準は極めてシンプルです。
- `interface` を使う時: 公開APIやライブラリの境界線で、オブジェクトの形状を定義し、宣言の結合による拡張性が必要な場合のみ。
- `type` + 関数 を使う時: それ以外。つまり、ビジネスロジック、データ変換、非同期処理のパイプラインなど、コードの大部分。
「オブジェクトにメソッドを詰め込むのは、古いJavaの悪癖をTypeScriptに持ち込むこと」です。
TypeScriptの型システムは、単なるバリデーターではありません。それはコードの構造を決定づける強力なアーキテクチャ設計図です。メソッドを独立させ、関数としてパイプラインに流し込む。この意識を持つだけで、あなたのコードの保守性は劇的に向上し、バグの温床となる `this` の呪縛から解放されるはずです。
さあ、明日からのコードレビューで、不要な `interface` メソッドを削ぎ落とすことから始めてみてください。それが、真のTypeScriptマスターへの第一歩です。