interfaceの基本は理解したけど、「似た型をいくつも定義していて、なんか無駄だな…」と感じることはないですか?

interfaceのextendsって何をするもの?
「拡張」と「継承」ってどう違うの?
プロパティを上書きしようとしたらエラーになった…なんで?

この記事では、interfaceのextends構文を使って型を拡張・継承する方法を解説します。「なぜ拡張が必要か」という動機から、基本の書き方、複数のinterfaceを継承する方法、プロパティのオーバーライドの注意点まで一通り説明します。

この記事は次のような方におすすめです。

この記事はこんな人におすすめ!
  • interfaceの基本的な書き方は理解した人
  • extendsを使って型を拡張したいが書き方がわからない人
  • 複数のinterfaceをまとめて継承したいと思っている人
  • プロパティを上書きしようとしてエラーが出た人

この記事を読むと、interfaceのextendsを使って型を柔軟に再利用できるようになります。

それでは、順を追って詳しく見ていきましょう!

まずは動画解説を観る

interfaceの拡張(extends)とは何か

TypeScriptを書いていると、「既存のinterfaceに少しプロパティを足した型が欲しい」という場面がよく出てきます。

たとえば、Userという型があって、そこにroleプロパティを追加したAdminUserが必要になったとします。一番単純な解決策はコピペですが、それだとUserの定義を変えたときAdminUserも直さないといけません。

// コピペで管理すると、変更が2か所に増えてしまう
interface User {
  id: number;
  name: string;
  email: string;
}

interface AdminUser {
  id: number;
  name: string;
  email: string;
  role: string; // ← これを追加したかっただけなのに…
}

extendsを使えば、既存のinterfaceのプロパティをそのまま引き継いで、新しいinterfaceを作れます。 変更も1か所にまとめられます。

この「既存の型を土台にして新しい型を作る」のがextendsによる拡張です。

なかむぅ
なかむぅ
interfaceの基本的な書き方はこちらで確認できます。
TypeScriptのinterfaceとは?オブジェクト型定義の基本と使い方TypeScriptのinterfaceの基本から使い方、typeとの違いまで初心者向けにわかりやすく解説します...

extendsの基本の書き方

extendsの基本的な書き方はシンプルです。

interface 拡張後のinterface名 extends 拡張元のinterface名 {
  // 追加するプロパティ
}

先ほどの例で書き直してみます。

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

interface AdminUser extends User {
  role: string;
}

AdminUser extends Userと書くことで、Userのプロパティをそのまま引き継いだ上で、roleプロパティを追加しています。

ちなみに、TypeScriptのextendsキーワードはジェネリクスの型制約(<T extends ...>)など、別の文脈でも登場します。ジェネリクスでのextendsについては別の記事をご覧ください。

なかむぅ
なかむぅ
ジェネリクスでのextendsはこちらで解説しています。
【TypeScript】ジェネリクス(generics)とは?書き方と3つの使いどころ
【TypeScript】ジェネリクス(generics)とは?書き方と3つの使いどころTypeScriptのジェネリクスとは何かを初心者向けに解説。の意味、anyとの違い、書き方や使いどころまでやさしく理解できます。...

拡張後に何が変わるのか

AdminUser extends Userと書くと、AdminUserUserのプロパティをすべて持つ型になります。コードで確認してみましょう。

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

interface AdminUser extends User {
  role: string;
}

const admin: AdminUser = {
  id: 1,
  name: "田中",
  email: "tanaka@example.com",
  role: "admin", // Userのプロパティも、追加したプロパティも必要
};

AdminUser型の変数には、Userのプロパティ(idnameemail)もroleも全部必要です。どれか欠けるとTypeScriptがエラーを出してくれます。

extendsすると、継承元のプロパティがすべて引き継がれる、というのが基本の動作です。

プロパティを追加して拡張する

実際の使い方をもう少し具体的に見ていきます。

たとえばECサイトで、一般ユーザーと管理者ユーザーで型を分けたいとします。

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

// 一般ユーザー向けの追加情報を持つ型
interface CustomerUser extends User {
  purchaseHistory: string[];
}

// 管理者向けの追加情報を持つ型
interface AdminUser extends User {
  role: "admin" | "moderator";
  lastLoginAt: Date;
}

Userを1か所で管理しつつ、それぞれの型に必要なプロパティを追加できます。Userの定義を変えても、CustomerUserAdminUserに自動で反映されるので、修正漏れのリスクが減ります。

オプショナルプロパティ(?)を追加することも当然できます。

interface AdminUser extends User {
  role: string;
  department?: string; // あってもなくてもいいプロパティ
}

複数のinterfaceをまとめて継承する

extendsには複数のinterfaceをまとめて継承する書き方もあります。カンマで区切るだけです。

interface HasTimestamps {
  createdAt: Date;
  updatedAt: Date;
}

interface HasPermissions {
  permissions: string[];
}

interface AdminUser extends User, HasTimestamps, HasPermissions {
  role: string;
}

AdminUserUserHasTimestampsHasPermissionsの3つのプロパティをすべて引き継ぎます。

よく使う属性(タイムスタンプや権限など)を小さいinterfaceに切り出しておくと、組み合わせて使えて便利です。

ひとつ注意点として、複数のinterfaceを継承するとき、それぞれのinterfaceに同じ名前のプロパティがあって型が違う場合はエラーになります。継承元同士でプロパティ名が重複しないよう設計しましょう。

プロパティのオーバーライドには注意が必要

継承元にあるプロパティを拡張先で上書き(オーバーライド)したい場面があるかもしれません。TypeScriptではできる変更とできない変更があります。 この区別を理解しておくと、エラーを出さずに済みます。

型を狭める変更(部分型への変更)はできる

継承元のプロパティをより具体的な型に変更することはOKです。

interface User {
  id: number;
  name: string;
  status: string; // stringという広い型
}

interface ActiveUser extends User {
  status: "active" | "inactive"; // より具体的なリテラル型に変更
}

"active" | "inactive"stringの中に含まれる(部分型である)ので、TypeScriptは許可します。

interface Shape {
  color: string;
}

interface ColoredCircle extends Shape {
  color: "red" | "blue" | "green"; // stringをリテラル型に絞り込み
  radius: number;
}

このように、型を広い方から狭い方へ変更する(部分型への変更)はextendsでも問題なくできます。

互換性のない型への変更はできない

一方で、継承元の型と互換性のない型への変更はエラーになります。

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

interface WeirdUser extends User {
  name: number; // エラー!stringをnumberには変更できない
}

nameの型をstringからnumberに変更しようとすると、TypeScriptがエラーを出します。numberstringの部分型ではないからです。

もし「継承元のプロパティを完全に別の型にしたい」という場面があれば、Omitを使って一度プロパティを除外してから再定義するという方法があります。

// Omitで name を除外してから再定義する
interface WeirdUser extends Omit<User, "name"> {
  name: number; // これならOK
}
なかむぅ
なかむぅ
Omitの詳しい使い方はユーティリティ型の記事で解説しています。
TypeScriptでよく使う5つのユーティリティ型(utility types)の使い方TypeScriptのユーティリティ型を初心者向けに解説。Partial・Pick・Omitなど、よく使う5つの型の意味と使い分けがすっきりわかります。...

【付録】さらに学びを深めるためのリソース


さらにTypescriptの学習を進めたい方のために、いくつかのリソースを紹介します。
これらのリソースを活用することで、TypeScriptの型システムについてより深い知識を得ることができるでしょう。

おすすめの書籍

ゼロからわかる TypeScript入門


技術評論社から出版されている「ゼロからわかる TypeScript入門」は、プログラミング初心者や本職プログラマーではない方を主な対象にした入門書です。

変数・条件分岐・ループといった基本から、クラスやインターフェース、モジュールまで段階的に学べる構成になっています。最終章ではWeb APIとJSONを使った非同期Webアプリの作成も体験できるので、「実際に動くものを作る」ところまで到達できます。

プロを目指す人のためのTypeScript入門


技術評論社の「プロを目指す人のためのTypeScript入門 安全なコードの書き方から高度な型の使い方まで」、通称 ブルーベリー本 です。
JavaScriptの仕様とTypeScript独自の機能を両方押さえつつ、リテラル型・ユニオン型・keyof型・ジェネリクスなど、高度な型表現まで踏み込んで解説しています。TypeScriptの型システムの表現力を本格的に学べる一冊です。

オンラインで参照できる公式ドキュメント

TypeScript公式ハンドブック


https://www.typescriptlang.org/docs/
TypeScriptの公式ドキュメントです。
intersection型を含む、すべての型システムの機能について詳細な説明があります。

TypeScript Deep Dive


https://basarat.gitbook.io/typescript/
TypeScriptの深い部分まで掘り下げて解説しているオンラインブックです。
無料で読むことができ、intersection型についても詳しく説明されています。

TypeScriptの学習は終わりがありません。
新しい機能が常に追加され、より良い書き方が発見されています。
継続的に学習を続けることで、より良いTypeScriptプログラマーになれるはずです。


まとめ — extendsで型を再利用しよう

この記事で解説した内容をまとめます。

  • extendsを使うと、既存のinterfaceのプロパティを引き継いで新しいinterfaceを定義できる
  • 基本の書き方はinterface B extends A { ... }
  • プロパティの追加は自由にできる
  • カンマ区切りで複数のinterfaceを同時に継承できる(interface C extends A, B
  • プロパティのオーバーライドには制約がある
    • 型を狭める変更(部分型への変更)はOK
    • 互換性のない型への変更はエラー

共通部分をひとつのinterfaceにまとめてextendsで使い回す、という設計は実際のコードでよく登場します。「似たような型を何度も書いている」と感じたら、extendsで整理できないか試してみてください。

なかむぅ
なかむぅ
interfaceとtypeをどう使い分けるかが気になる方はこちらもどうぞ。
TypeScriptのinterfaceとtypeの違いって?3つの違いと使い分け方TypeScriptのinterfaceとtypeの違いを3つのポイントで整理。ユニオン型や拡張の書き方など、使い分けの判断基準がすっきりわかります。...

※本記事の本文案はAIを活用して作成していますが、記載している内容およびコードは筆者が実際に調査、検証・実行し、内容の正確性を確認した上で公開しています。