TypeScript PR

TypeScriptのtypeとinterfaceを使い分ける方法:違いと使いどころを徹底解説

記事内に商品プロモーションを含む場合があります

TypeScriptでよく使われるtypeinterface。どちらも型を定義するために使用しますが、それぞれ異なる特徴や使い方があります。「typeinterface、どっちを使えばいいの?」という疑問にお答えするため、このブログでは以下のポイントを初心者にも分かりやすく解説します。

  1. それぞれの基本的な使い方
  2. 違いと特徴
  3. 実際にどう使い分けるべきか

型の定義

interface

interfaceはオブジェクトの構造を定義するのに便利な機能です。特にオブジェクト型に対して多くの機能が用意されています。

interface User {
  name: string;
  age: number;
}

const user: User = {
  name: "Taro",
  age: 25,
};

interfaceの特徴

  • オブジェクト型専用の機能。
  • 再利用性が高く、extendsによる拡張が可能。
  • 型のマージができる(後述)。

type

typeは型エイリアスを作成するための機能です。interfaceよりも幅広い型を定義できます。

type User = {
  name: string;
  age: number;
};

type ID = string | number;

const userId: ID = "abc123"; // 文字列または数値を許容

typeの特徴

  • オブジェクト型以外の型(プリミティブ型、ユニオン型、タプル型など)も定義可能。
  • 柔軟性が高く、複雑な型操作に向いている。

interfacetypeの違い

両者は似ていますが、いくつかの重要な違いがあります。

宣言と代入

interfaceは型の「宣言」として扱われるため、同じ名前のinterfaceを複数回宣言するとマージ(結合)されます。一方、typeは型の「代入」として扱われるため、同じ名前で再定義することはできません。

// interfaceのマージ
interface Example {
  prop: string;
}
interface Example {
  anotherProp: number;
}

const example: Example = {
  prop: "hello",
  anotherProp: 123,
};

// typeの再定義はエラー
type ExampleType = { prop: string };
// type ExampleType = { anotherProp: number }; // エラー

定義できる型の種類

interfaceはオブジェクト型の定義に特化していますが、typeはより柔軟で、ユニオン型、タプル型やプリミティブ型など、幅広い型を定義できます。

// typeで定義可能なユニオン型
type ID = string | number;

// typeでタプル型を定義
type Coordinates = [number, number];

type Color = "red" | "blue" | "green";

拡張

interfaceextendsを使って継承できます。一方、type&を使って型を合成します。

// interfaceで拡張
interface Person {
  name: string;
}
interface Employee extends Person {
  employeeId: number;
}

const employee: Employee = {
  name: "Taro",
  employeeId: 123,
};

// typeで型を合成
type PersonType = { name: string };
type EmployeeType = PersonType & { employeeId: number };

const employeeType: EmployeeType = {
  name: "Taro",
  employeeId: 123,
};

再定義・型のマージに関する注意点

  • 再定義: interface は同名の型を複数回宣言可能(型がマージされる)が、type では同名の型を再定義するとエラーとなる。
// interfaceでのマージ例
interface Example {
  a: number;
}
interface Example {
  b: string;
}
const obj: Example = { a: 1, b: "text" }; // 有効

// typeでのエラー例
type Example = { a: number };
type Example = { b: string }; // エラー: 'Example' が重複しています

補足例

  1. 型の柔軟性を示すコード例
// typeでユニオン型やタプル型の定義例
type ApiResponse = { success: boolean } | { error: string };
type Coordinates = [number, number];

  1. 拡張方法の比較
// interfaceの拡張
interface Person {
  name: string;
}
interface Employee extends Person {
  employeeId: number;
}

// typeの型合成
type PersonType = { name: string };
type EmployeeType = PersonType & { employeeId: number };

どちらを使う?

typeinterfaceのどちらを使うべきかは、用途やチームのコーディングスタイルによって異なります。それぞれの特徴を理解して適切に使い分けましょう。

interface

  • 理由:
    • オブジェクト指向の設計に馴染む。
    • 型の拡張性が高い。
    • TypeScriptライブラリやフレームワークでよく使用される。
  • 使用例
interface ComponentProps { title: string; description?: string; // オプショナルプロパティ }

type

  • 理由:
    • 汎用性が高い。
    • ユニオン型やタプル型を多用する場合に便利。
    • typeで一貫した書き方を好む場合。
  • 使用例
type ApiResponse = { data: any } | { error: string };

まとめ

TypeScriptのtypeinterfaceはどちらも型を定義するために使われますが、それぞれの特徴や用途には明確な違いがあります。

  • interface:
    • オブジェクト型に特化しており、型の拡張や再利用性が高い。
    • TypeScriptが推奨する標準的な型定義方法として多くの場面で使用される。
  • type:
    • オブジェクト型以外にもユニオン型やタプル型を定義できる汎用性がある。
    • 柔軟性が高く、複雑な型を操作する際に便利。

使い分けの指針:

  • オブジェクト型を中心とした構造にはinterfaceを使う。
  • プリミティブ型やユニオン型、タプル型などの柔軟性が必要な場面ではtypeを選ぶ。

最終的には、プロジェクトのコーディングスタイルやチームの好みに応じて一貫性を持たせることが重要です。このブログを参考に、自分の開発に最適な方法を選んでみてください!