こんにちは。TypeScriptの世界へようこそ。
型システムという強力な武器を手にすると、私たちは「変化に強いプログラム」を書けるようになります。今日は、TypeScriptの「Interfaceの宣言マージ」という、一見すると地味な、しかし大規模開発において最強の武器となる機能を深掘りしていきましょう。
「コア(核)となる部分は触らずに、後から機能を追加する」――そんな魔法のようなプラグインアーキテクチャを、どうやって型安全に実現するか。一緒に紐解いていきましょうね。
—
1. 宣言マージ(Declaration Merging)の正体
TypeScriptの `interface` には、他の言語にはあまり見られない面白い性質があります。同じ名前の `interface` を複数回宣言すると、コンパイラがそれらを一つに統合(マージ)してくれるのです。
まずは、基本の動作を見てみましょう。
// コアモジュールで定義されたインターフェース
interface User {
id: number;
name: string;
}
// 別の場所(プラグインなど)で同じ名前を宣言
interface User {
email: string; // ここでプロパティが追加される
}
// 結果として、Userは「id, name, email」すべてを持つ型になる
const user: User = {
id: 1,
name: “Taro”,
email: “taro@example.com”
};
これ、実はすごいことなんです。「後から定義を足しても、コンパイラがそれを理解して型チェックしてくれる」。この性質こそが、プラグインシステムを作るための鍵になります。
—
2. 実践:プラグインアーキテクチャの構築
例えば、あなたが「画像処理ライブラリ」を作ったとしましょう。ユーザーは「リサイズ機能」だけを使いたいかもしれないし、「フィルター加工機能」も欲しいかもしれません。
コア部分をいじらずに、プラグインで機能拡張する構造を作ってみましょう。
手順①:コア側の定義(`core.ts`)
まず、プラグインが「どこに機能を追加できるか」の入り口(インターフェース)を用意します。
// コアモジュールのインターフェース
export interface PluginRegistry {
// ここにプラグインが自身の機能を追加していく
}
export class App {
plugins: PluginRegistry = {};
register(name: string, plugin: any) {
this.plugins[name] = plugin;
}
}
手順②:プラグインによる拡張(`plugin-blur.ts`)
ここで「宣言マージ」の出番です。コアの `PluginRegistry` を拡張します。
// 外部プラグインがコアの型定義を拡張する
declare module “./core” {
interface PluginRegistry {
blur: (radius: number) => void;
}
}
// 実装
const blurPlugin = (radius: number) => console.log(`Blurring with ${radius}`);
手順③:利用側
コンパイラが型をマージしてくれるので、呼び出し側は「後付けされた機能」であることを意識せず、型安全に利用できます。
const app = new App();
app.register(“blur”, blurPlugin);
// 型定義のおかげで、IDEは blur の存在を知っている!
app.plugins.blur(5);
—
3. 陥りやすい罠:ここだけは注意!
初学者がよくやってしまうミスが、「Type Alias(型エイリアス)でやろうとしてしまう」ことです。
type User = { id: number };
type User = { email: string }; // エラー!「識別子 ‘User’ は既に宣言されています」
`type` は宣言マージができません。これはTypeScriptの設計思想によるものです。`interface` は「オブジェクトの形状(構造)」を定義するものとして、あえてこの「オープンな拡張性」が持たされています。
「構造を拡張したいなら interface、複雑な合成や計算をしたいなら type」。この使い分けを意識するだけで、あなたの設計レベルは一気に向上します。
—
4. なぜこれが「最強の設計」なのか
この手法の素晴らしい点は、「依存の向き」が逆転していることです。
通常、コアがプラグインを知っている必要がありますが、宣言マージを使えば、コアは「型定義の枠組み」だけを提供し、プラグイン側が勝手に「自分の枠」をそこに書き込むことができます。
- 疎結合: コアのコードを一切修正せずに機能追加できる。
- 型安全: 拡張した機能に対しても、TypeScriptの強力な補完とチェックが効く。
- メンテナンス性: 大規模なプロジェクトにおいて、機能単位での分割コンパイルが可能になる。
—
最後に:TypeScriptを掌握するということ
宣言マージは、TypeScriptの型システムが「コンパイル時にどのようにコードを統合し、解釈しているか」という本質を知らなければ使いこなせません。
最初は魔法のように見えるかもしれませんが、裏側ではコンパイラがせっせと抽象構文木(AST)をマージし、あなたのために型情報を組み立てています。「型はただのチェック用」ではなく、「システムの設計図そのもの」として扱う。それが、TypeScriptをマスターするための第一歩です。
ここをクリアできれば、あなたはもう単なるライブラリ利用者ではなく、フレームワークを設計する側の視点を持てたはずです。自信を持って、どんどん型を拡張していってくださいね!
何かわからないことがあれば、またいつでも聞きに来てください。応援していますよ。