- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
本記事は、オープンソース書籍『The Concise TypeScript Book』の第 53 章「マージと拡張(Merging and Extension)」を深掘りし、TypeScript の型システムを扱う上で欠かせない「マージ(Merging)」と「拡張(Extension)」という 2 つの概念を、実例とともに解説します。本章は同名の宣言を 1 つにまとめる宣言マージと、既存の型やインターフェースを継承・拡張して新たな型を作る仕組みに焦点を当てており、これらを読み解けば、interfaceとtypeの使い分け、ライブラリの型定義を壊さずに拡張する技法まで身につきます。
マージ(Merging):同じ名前の複数の宣言を 1 つの定義に統合する
「マージ」とは、同じ名前を持つ複数の宣言を 1 つの定義にまとめる機能です。最も典型的な例が、同じ名前のインターフェースを複数回定義するケースです。
本章が示す基本例を見てみましょう。
interface X { a: string; } interface X { b: number; } const person: X = { a: 'a', b: 7, };このコードでは、interface Xが 2 回宣言されています。TypeScript はこれらを単一のインターフェースにマージし、結果としてXはa: stringとb: numberの両方のプロパティを持つものとして扱われます。そのため、personにはaとbの両方を必ず指定する必要があります。
この「宣言マージ」は、TypeScript のinterface固有の強力な機能です。同じ名前のinterfaceを複数宣言してもエラーにはならず、すべての宣言が持つプロパティとメソッドが合体した単一の定義になります。
型エイリアス(type)では宣言マージできない
一方で、typeエイリアスは宣言マージをサポートしません。本書の第 54 章「型とインターフェースの違い(differences-between-type-and-interface.md)」でも指摘されている通り、typeで同名の宣言を複数回行うとコンパイルエラーになります。
// これはエラーになる type A = { x: string; }; type A = { y: string; }; // エラー: 識別子 'A' は重複していますこの違いは、「元の定義を変更せずに追加機能を加えたい」「不足している型や誤った型にパッチを当てたい」といった場面で特に重要になります。ライブラリが公開している型をそのまま書き換えることはできませんが、宣言マージを使えば**外部ライブラリの型を拡張(augmentation)**できます。
宣言マージの実践的な活用:外部型へのパッチ
宣言マージは、例えばサードパーティライブラリが提供するinterfaceに独自のプロパティを追加する場合に威力を発揮します。
// ライブラリ側が定義している型(変更できない) interface Window { title: string; } // 自プロジェクト側でマージして拡張 interface Window { version: string; customProperty?: number; } // これで window.version にアクセスできるこのように、既存の定義を一切変更せずに、型システムのレベルで宣言を追加できます。宣言マージの対象となるのはinterfaceが主ですが、classと同名のinterfaceを宣言してクラスに静的メンバーや追加契約を持たせるといった使い方(クラスとインターフェースのマージ)も可能です。クラス全般の仕組みについては第 55 章「クラス(class.md)」を参照してください。
拡張(Extension):既存の型から新しい型を作る
「拡張」とは、既存の型またはインターフェースを拡張・継承して新しい型を作成する機能です。元の定義を変更せずに、既存の型へ追加のプロパティまたはメソッドを加える仕組みであり、本章ではinterfaceのextendsキーワードによる拡張が例示されています。
interface Animal { name: string; eat(): void; } interface Bird extends Animal { sing(): void; } const dog: Bird = { name: 'Bird 1', eat() { console.log('Eating'); }, sing() { console.log('Singing'); }, };BirdはAnimalをextendsしており、nameとeat()を継承した上で、独自のsing()を追加しています。その結果、Bird型の値はAnimalのすべてのメンバーを満たしつつ、sing()も実装しなければなりません。
複数の型からの拡張
interfaceは複数の型を一度に拡張することもできます。本書の第 16 章「型の拡張(extending-types.md)」には、次のような例があります。
interface A { a: string; } interface B { b: string; } interface Y extends A, B { y: string; }この場合Yはa、b、yのすべてのプロパティを持つことになります。複数のinterfaceを組み合わせて契約を合成できるのがポイントです。
extends は interface と class にのみ有効
重要な制約として、extendsキーワードはinterfaceとclassに対してのみ機能します。typeエイリアスを拡張するには、交差型(&演算子)を使う必要があります。
type A = { a: number; }; type B = { b: number; }; type C = A & B;Cはaとbの両方を持つ交差型(Intersection Type)になります。交差型の詳細は第 33 章「交差型(intersection-types.md)」で解説されています。
片方向の拡張:interface は type を拡張できる
さらに、interfaceでtypeを拡張することはできますが、その逆(typeがinterfaceを&でなく直接拡張すること)はできません。
type A = { a: string; }; interface B extends A { b: string; }interface Bはtype Aの構造を継承しつつbを追加できるため、既存のtypeをベースに契約を広げたい場合に便利です。
ユニオン型は extends できない
interfaceはユニオン型のような複雑な型をextendsすることはできません(第 54 章より)。ユニオン型を組み合わせたい場合は、typeエイリアスと|演算子を使うのが正攻法です。
type Department = 'dep-x' | 'dep-y'; // ユニオン型インターフェース同士のユニオンを表現したい場合も、typeを使って間接的に定義します(例:type C = A | B;)。交差型・ユニオン型を自由に組み立てられるのはtypeの大きな柔軟性であり、第 54 章ではこの点が明記されています。
型の拡張におけるもう一つの道具:& 演算子による交差型
拡張の考え方をtypeの世界で実現するのが交差型です。第 54 章では、既存のinterfaceに対してtypeを交差させる例も示されています。
interface A { x: string; y: number; } type B = A & { j: string; }; const c: B = { x: 'x', y: 123, j: 'j', };interface Aの構造をそのままに、type Bが追加プロパティjを加えた複合型になっています。この書き方は「元の定義を変更せずに型を拡張する」という拡張の目的を、typeの文法で実現する代表的なパターンです。
第 54 章「型とインターフェースの違い」では、interfaceが交差型を組み込みでサポートせず、ユニオン型も間接表現に留まる一方、typeは|と&の両演算子で柔軟に合成できる、という対比が詳述されています。
マージと拡張の違いまとめ
| 観点 | マージ(Merging) | 拡張(Extension) |
|---|---|---|
| 意味 | 同じ名前の複数の宣言を 1 つの定義に統合 | 既存の型を継承・合成して新しい型を作成 |
| 主な対象 | interface(同名複数宣言) | interfaceのextends、typeの&(交差型) |
| 元の定義 | 変更せずに宣言を追加 | 変更せずに新しい型を作る |
typeでの実現 | 不可(宣言マージ非対応) | 交差型&で実現 |
| 代表的な用途 | ライブラリ型へのパッチ、追加機能の付与 | 共通契約の継承、複合型の構築 |
どちらも「既存の型定義を書き換えずに型の世界を広げる」という点では共通しますが、マージは同名宣言の合体、拡張は新しい型の創出という違いがあります。この 2 つを区別できると、interfaceとtypeの選択基準も明確になります。
まとめ:マージと拡張を使い分ける
『The Concise TypeScript Book』の「マージと拡張」の章が伝える本質は、次の 2 点に要約できます。
- マージ:同じ名前の
interfaceを複数回宣言すれば、TypeScript が自動的に 1 つの定義へ統合してくれる。これはtypeにはない能力であり、外部ライブラリの型を拡張する際に強力。 - 拡張:
interfaceはextendsで既存の型(複数可)を継承し、typeは交差型&で複数の型を合成する。interfaceはtypeを拡張できるが、その逆はできない。
型定義を設計する際は、まず「同じ名前で宣言を追加したいのか(→ マージ)」それとも「既存の型から派生した新しい型が欲しいのか(→ 拡張)」を判断すると、自然と正しい構文を選べるようになります。
本記事の内容は、以下の関連章とあわせて読むことでより深く理解できます。
- 型とインターフェースの違い:differences-between-type-and-interface.md
- 型の拡張:extending-types.md
- 交差型:intersection-types.md
- インターフェースと型の基本構文:interface-and-type.md
- クラスと継承:class.md
- 文档
- 教程
【免费下载链接】typescript-book
The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source.
相关推荐
The Concise TypeScript Book 完全ガイド:interface と type の使い分けと型定義の実践
The Concise TypeScript Book 完全ガイド:interface と type の使い分けと型定義の実践 インターフェース( interf
文档教程voice-changerのプラグインアーキテクチャ:独自機能を追加する拡張開発ガイド
voice changerのプラグインアーキテクチャ:独自機能を追加する拡張開発ガイド はじめに:voice changerのプラグイン開発のニーズ リアルタイ
人工智能语音模型推理服务深度学习voice-changerの拡張性:カスタムモデルを追加するプラグイン開発ガイド
voice changerの拡張性:カスタムモデルを追加するプラグイン開発ガイド はじめに:voice changerのモデル拡張の課題と機会 リアルタイムボイ
人工智能语音模型推理服务深度学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考