😸

TypeScript における `interface` と `type` の使い分け方

に公開

はじめに

TypeScript ではオブジェクトやデータ構造に型を付ける方法として、interfacetype という 2 つの仕組みが用意されています。
どちらも「型を定義する」という目的は同じですが、設計上の意図や拡張性、ユースケースによって使い分けることで、より保守性の高いコードを書くことができます。


TypeScript の型定義の基本

TypeScript は JavaScript に型システムを追加した静的型付き言語です。

  • interface:オブジェクトの形(プロパティやメソッド)を定義するための構文
  • type:任意の型(プリミティブ、オブジェクト、ユニオン、タプル、関数など)に名前を付けるための仕組み

どちらも型安全性を高め、補完の信頼性を上げる役割があります。


学んだこと

1. オブジェクトの構造定義と拡張

interface はオブジェクト構造を宣言するために作られており、他の interfaceextends で継承できます。
さらに Declaration Merging という特徴があり、同じ名前で複数定義すると自動的にマージされます。

interface Window {
  title: string;
}

interface Window {
  version: number;
}

// → Window は title と version の両方を持つ型になる

ライブラリの API 型を拡張する場合に便利ですが、意図せず型が広がる可能性もあるため注意が必要です。

2. 型別名としての柔軟性

type はあらゆる型に名前を付けられるので、オブジェクト以外にもユニオン型やタプル、プリミティブ型など柔軟に扱えます。
また & を使って型の合成(交差型)も可能です。

// ユニオン型
type Status = "idle" | "loading" | "success" | "error";

// タプル型
type Point = [number, number]; // x, y の座標を表す

// プリミティブ型
type UserName = string;
type UserAge = number;

// 交差型(インターセクション型)
type WithID<T> = T & { id: string };

複雑な型構造や、オブジェクト以外の型操作が必要な場合に便利です。

3. 宣言マージの可否

  • interface:同名で複数定義すると型が自動で結合される
  • type:同名再宣言はエラー、後から拡張できない

この点が両者の大きな違いです。

4. 実務での選択基準

  • 基本的なオブジェクト型 → interface
  • ユニオン型や複雑型 → type
  • ライブラリ API 定義 → interface

もちろん、どちらでも表現できるケースもあります。その場合はプロジェクトの方針で統一すると、可読性や一貫性が高まります。


気づき

interfacetype は一見似ていますが、設計意図や用途に応じて使い分けるとコードの健全性が変わります。

  • interface:拡張性が高く、API 契約のような役割に向く
  • type:柔軟性が高く、複雑型やユニオン型に最適

極端に制限する必要はなく、コードの目的と変更可能性を意識して適材適所で使うことが、保守性や開発者体験の向上につながります。


余談:結局どちらを使えば良いの?

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.
大部分の場合は、個人の好みに基づいて選んで構いません。TypeScript は、もし別の種類の宣言が必要であればそれを教えてくれます。
もし目安としての指針が欲しいなら、type の機能が必要になるまで interface を使いなさい。

ということで、公式に従うなら大部分の場合、interfaceとtypeは好みで選んでよいとのことです。
また、普段はinterfaceをデフォルトで選び、typeでしかできない特殊な表現が必要なときだけtypeを使うというのが結論でした。

ほとんどの場合、interface宣言はtype文で代用可能です。しかもtype文のほうがより多くの場面で使えるので、interface宣言は使用せずにtype文のみを使うという流儀もあるようです(筆者もそうです)。

とのことで、type文を推しているような記述が見られました。

個人的には、interface文特有のオープンエンドが、大規模開発の時に悪さをするような気がするので、type文を優先して使うを支持します。

Tips

ESLint で型定義を統一&強制できます。
@typescript-eslint/consistent-type-definitions ルールを使うと、interface と type の使い分けを自動チェックできます。

{
  "rules": {
    "@typescript-eslint/consistent-type-definitions": ["error", "type"]
  }
}

このルールを設定すると、オブジェクトの型定義で interface を使った場合にエラーが出るようになります。


参考文献

https://qiita.com/Yasushi-Mo/items/44fc4b678cfc19545a93
https://www.geeksforgeeks.org/typescript/typescript-differences-between-type-aliases-and-interfaces-type
https://zenn.dev/begineer/articles/12894fd90718bd
https://blog.logrocket.com/types-vs-interfaces-typescript/

Discussion