TypeScript | 基礎文法:オブジェクト基礎 – type と interface の違い

TypeScript
スポンサーリンク

まずざっくり:「どっちも“型を定義する道具”」

typeinterface も、「型に名前をつける」ための仕組みです。
どちらもオブジェクトの形を表現できます。

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

type User2 = {
  id: number;
  name: string;
};
TypeScript

この2つは、ほぼ同じ意味です。
だからこそ、初心者が最初に混乱するのは自然です——「違いがあるようで、ないように見える」から。

違いは主に「拡張の仕方」「宣言のマージ」「表現できる型の幅」にあります。
ここを、実際のコードと一緒にかみ砕いていきます。


共通点:どちらも「オブジェクトの形」を表現できる

オブジェクト型としての使い方はほぼ同じ

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

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

const p1: Person = { name: "Taro", age: 20 };
const p2: PersonType = { name: "Hanako", age: 18 };
TypeScript

どちらも「name: stringage: number を持つオブジェクト」という意味です。
このレベルでは、どちらを使ってもほぼ差はありません

だから実務でも、「プロジェクトとしてどちらかに寄せる」「チームのルールに従う」といった運用が多いです。


大きな違い1:宣言マージ(同じ名前をあとから足せるか)

interface は「あとから足せる」

interface User {
  name: string;
}

interface User {
  age: number;
}

const u: User = {
  name: "Taro",
  age: 20,
};
TypeScript

同じ名前の interface User を2回書くと、自動的にマージされて1つの型になります
結果として User{ name: string; age: number } という形になります。

これは「宣言マージ」と呼ばれる機能で、interface の大きな特徴です。

type は「同じ名前で2回は書けない」

type User = {
  name: string;
};

type User = {
  age: number;
}; // エラー:同じ名前で再定義はできない
TypeScript

type は同じ名前で複数回宣言できません。
「1つの名前に1つの定義だけ」というルールです。

まとめると:

  • 「あとから同じ名前にプロパティを足したい」→ interface ならできる
  • 「1つの名前に1つの定義だけでいい」→ type でも問題ない

宣言マージは便利ですが、やりすぎると「どこで何が足されているか分かりにくくなる」ので、
大規模なコードベースでは慎重に使われます。


大きな違い2:拡張の書き方(extends と &)

interface の拡張(extends)

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

interface AdminUser extends User {
  permissions: string[];
}

const admin: AdminUser = {
  id: 1,
  name: "Taro",
  permissions: ["user:read", "user:write"],
};
TypeScript

extends を使って、「既存の interface をベースに新しい interface を作る」ことができます。
オブジェクト指向の「継承」に近い感覚です。

type の拡張(交差型 &)

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

type AdminUser = User & {
  permissions: string[];
};
TypeScript

type では &(交差型)を使って、「型同士を合成」します。
結果として意味はほぼ同じですが、書き方のスタイルが少し違います。

感覚としては:

  • 「クラスっぽく継承したい」「オブジェクトの“形”を素直に伸ばしたい」→ interfaceextends
  • 「いろんな型を合成して1つにしたい」「ユニオンや交差を駆使したい」→ type&|

という使い分けがしっくりきます。


大きな違い3:表現できる型の幅(type の方が守備範囲が広い)

type は「何にでも名前をつけられる」

type UserId = number;
type Status = "success" | "error" | "loading";
type Point = [number, number];
type Handler = (value: string) => void;
TypeScript

type は、プリミティブ型・ユニオン型・タプル・関数型など、ほぼ何にでも名前をつけられます

一方、interface は基本的に「オブジェクトの形」を表現するためのものです。
(関数型も書けますが、ユニオン型などは扱えません。)

interface Handler {
  (value: string): void;
}
TypeScript

なので、

  • 「オブジェクトの形」→ interface でも type でも書ける
  • 「ユニオン型・タプル・プリミティブに名前をつけたい」→ type 一択

という整理になります。


実務でのざっくりした使い分けの指針

よく言われる方針(あくまで“目安”)

いろんな記事やスタイルガイドを総合すると、よく出てくる方針はこんな感じです。

  • 「プリミティブ・ユニオン・タプルなど“形以外”の型」→ type
  • 「オブジェクトの形(特にクラスと一緒に使うもの)」→ interface
  • 「迷ったらどちらでもいいが、プロジェクトのルールに合わせる」

Google のスタイルガイドなどでは、
「オブジェクトには interface、それ以外には type を使う」といった方針も紹介されています。

ただし、TypeScript の公式として「絶対の正解」はなく、
「チームで統一されていること」「自分たちが読みやすいこと」が一番大事だとよく言われます。


初心者向けの結論:「まずはこう決めてしまっていい」

いきなり全部の違いを完璧に覚える必要はありません。
最初のうちは、次のように割り切ってしまって大丈夫です。

  • 「オブジェクトの形に名前をつける」→ どちらかに統一(たとえば interface
  • 「ユニオン型・タプル・プリミティブに名前をつける」→ type
  • 「プロジェクトや教材がどちらかを推しているなら、それに合わせる」

大事なのは、「型に名前をつけて、意味をはっきりさせる」習慣そのものです。
typeinterface かで悩みすぎるよりも、
「このデータにはどんな形と意味があるのか」を型として表現していくことに、意識を多めに割いてほしい。

そのうえで、「宣言マージが必要だから interface にしよう」「ユニオンを使うから type にしよう」といった判断は、
少しずつ経験と一緒に積み上げていけば十分間に合います。

タイトルとURLをコピーしました