5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【TypeScript】interface と type の使い分けまとめ

Last updated at Posted at 2025-11-14

はじめに

この記事では、TypeScript における interfacetype の違いや使い分けのポイント、実務での活用方法などについてサンプルコードを交えながらまとめます。

interfacetype とは?

TypeScript で型を定義する方法として、interfacetype の2つが用意されています。

どちらもオブジェクトの形を定義できますが、それぞれに得意・不得意があり、適切な場面で使い分けることで、より型安全で保守性の高いコードを書くことができます。

▼ 例:オブジェクトの型定義

interface User {
  id: number;
  name: string;
}
// 推論: interface User

type User = {
  id: number;
  name: string;
};
// 推論: type User

基本的な書き方はほぼ同じですが、重要な違いがいくつかあります。

主な違い

1. 宣言のマージ(Declaration Merging)

interface は同じ名前で複数回宣言すると、自動的にマージされます。

interface User {
  name: string;
}

interface User {
  age: number;
}

const user: User = {
  name: "太郎",
  age: 25
};
// 推論: User は { name: string; age: number; } として扱われる

一方、type は同じ名前で複数回宣言できません。

type User = {
  name: string;
};

type User = {
  age: number;
};
// ❌ エラー: 重複した識別子 'User'

2. ユニオン型の定義

type はユニオン型を直接定義できますが、interface ではできません。

// ✅ type ではユニオン型を定義可能
type Status = "pending" | "success" | "error";
// 推論: "pending" | "success" | "error"

type Result = SuccessResult | ErrorResult;
// 推論: SuccessResult と ErrorResult のユニオン型

// ❌ interface ではユニオン型を定義できない
interface Status = "pending" | "success" | "error";
// 構文エラー

3. 継承と拡張

interfaceextends キーワードで継承できます。

interface Animal {
  name: string;
}

interface Dog extends Animal {
  bark(): void;
}
// 推論: Dog は name と bark を持つ

type&(インターセクション型)を使います。

type Animal = {
  name: string;
};

type Dog = Animal & {
  bark(): void;
};
// 推論: Dog は name と bark を持つ

使い分けの参考ポイント

以下のようなポイントが使い分けの参考になります。

シチュエーション 推奨 理由
オブジェクトの型定義 interface 拡張性が高く、宣言のマージが可能
ユニオン型 type interface では定義不可
プリミティブ型のエイリアス type interface では定義不可
関数の型定義 どちらでも チームで統一すればOK
ライブラリの公開API interface 利用者が拡張できる

開発者の認知負荷軽減のため、シチュエーションごとに厳密に使い分けることはせず、チームでどちらかに統一するという考えもあると思います。
ちなみに今の私が所属しているチームでは、基本的に type を使い、interface を使わないと実現できない型定義だけ interface を使うという方針にしています。

▼ 使用例1: APIレスポンスの型定義

// オブジェクトの構造を定義するので interface を使用
interface ApiResponse<T> {
  data: T;
  total: number;
  page: number;
}

interface User {
  id: number;
  name: string;
  email: string;
}

const response: ApiResponse<User[]> = {
  data: [{ id: 1, name: "太郎", email: "taro@example.com" }],
  total: 100,
  page: 1
};
// 推論: ApiResponse<User[]>

▼ 使用例2: ステート管理(ユニオン型)

// ユニオン型を含むので type を使用
type LoadingState = 
  | { status: "idle" }
  | { status: "loading" }
  | { status: "success"; data: string }
  | { status: "error"; error: Error };
// 推論: 4つの状態のユニオン型

// オブジェクトの型定義は interface
interface AppState {
  user: User | null;
  loadingState: LoadingState;
}

▼ 使用例3: Reactコンポーネントのprops

// オブジェクトの型定義なので interface を推奨
interface ButtonProps {
  label: string;
  onClick: () => void;
  variant?: "primary" | "secondary";
  disabled?: boolean;
}
// 推論: interface ButtonProps

const Button: React.FC<ButtonProps> = ({ 
  label, 
  onClick, 
  variant = "primary", 
  disabled 
}) => {
  return (
    <button 
      onClick={onClick} 
      disabled={disabled}
      className={variant}
    >
      {label}
    </button>
  );
};

ESLint で統一を強制できます。

@typescript-eslint/consistent-type-definitions ルールを使うと、interface と type の使い分けを自動チェックできます。

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

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

実践的な活用方法

ライブラリの型定義を拡張する

interface の宣言のマージを利用すると、ライブラリの型定義を拡張できます。

// 例: Windowオブジェクトにカスタムプロパティを追加
declare global {
  interface Window {
    gtag: (...args: any[]) => void;
    dataLayer: any[];
  }
}

// これで型エラーなく使える
window.gtag('event', 'page_view');
// 推論: Window.gtag は関数として認識される

複雑な型の組み合わせ

type を使うと、複雑な型操作が可能です。

type Nullable<T> = T | null;
// 推論: T または null

type ReadonlyDeep<T> = {
  readonly [K in keyof T]: T[K] extends object 
    ? ReadonlyDeep<T[K]> 
    : T[K];
};
// 推論: 再帰的に readonly にする型

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

type ImmutableUser = ReadonlyDeep<User>;
// 推論: すべてのプロパティが readonly

まとめ

interfacetype はそれぞれに特徴があり、適切に使い分けることで型安全性と保守性を高められます。

参考

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?