カテゴリ: TypeScript 更新日: 2026/01/09

TypeScriptとES6クラスのDI設計パターンを徹底解説!初心者のための依存性の注入

TypeScriptとES6クラスのDI設計パターンの応用例
TypeScriptとES6クラスのDI設計パターンの応用例

先生と生徒の会話形式で理解しよう

生徒

「TypeScriptのES6クラスを使っているときに、『DI(ディーアイ)』という言葉を聞きました。これって一体何のことですか?」

先生

「DIは『Dependency Injection(依存性の注入)』の略で、プログラムを部品化して、組み立てやすくするための設計手法のことですよ。」

生徒

「部品化……。なんだか難しそうですが、初心者でも使ったほうがいいのでしょうか?」

先生

「もちろんです!DIを知ると、修正に強く、テストもしやすい綺麗なコードが書けるようになります。まずは基本から一緒に見ていきましょう!」

1. DI(依存性の注入)とは何か?

1. DI(依存性の注入)とは何か?
1. DI(依存性の注入)とは何か?

プログラミングにおけるDI(Dependency Injection)とは、日本語で「依存性の注入」と訳されます。これだけ聞くと難しく感じますが、実は身近なものに例えると非常にシンプルです。

例えば、あなたが「ゲーム機」を持っているとしましょう。このゲーム機に、最初からひとつのソフトが溶接されていて、一生そのソフトしか遊べないとしたらどうでしょうか?これが「依存している(密結合)」状態です。別の遊びをしたくても、ゲーム機本体を買い直さなければなりません。

一方で、現代のゲーム機のように「カセットを差し替える」ことができる仕組みはどうでしょう。これがDI(注入)です。ゲーム機本体(クラス)は変えずに、外からカセット(機能)を差し込むことで、自由に動作を切り替えることができます。TypeScriptにおいて、このカセットの差し替えをスマートに行うのがDI設計パターンです。

2. ES6クラスの基本と「依存」の問題点

2. ES6クラスの基本と「依存」の問題点
2. ES6クラスの基本と「依存」の問題点

TypeScriptは、最新のJavaScript(ES6/ESNext)の機能をフルに活用できます。その代表がclass(クラス)です。クラスは、データと処理をまとめる「設計図」のようなものです。

まずは、DIを使わない「悪い例」を見てみましょう。ここでは、メールを送信する機能を持つクラスを考えてみます。


class EmailService {
    send(message: string) {
        console.log("メールを送信しました: " + message);
    }
}

class UserNotification {
    private service: EmailService;

    constructor() {
        // クラスの中で、直接EmailServiceを作ってしまっている
        this.service = new EmailService();
    }

    notify(message: string) {
        this.service.send(message);
    }
}

const notice = new UserNotification();
notice.notify("こんにちは!");

このコードの問題点は、UserNotificationクラスがEmailServiceベッタリと依存していることです。もし「メールではなくLINEで送りたい」となった場合、UserNotificationの中身を直接書き換えなければなりません。これは、パソコンを触り始めたばかりの方が「ひとつの部品を壊すと全部動かなくなる」という不安を感じる状態と同じです。

3. インターフェースを使ったDIの応用例

3. インターフェースを使ったDIの応用例
3. インターフェースを使ったDIの応用例

ここで、TypeScriptの強力な機能であるinterface(インターフェース)の出番です。インターフェースは「どんな機能を持っているか」という約束事(ルール)を決めるものです。これを使うことで、具体的な中身が何であれ、同じルールに従っていれば差し替えが可能になります。

先ほどの例を、DIパターンを使って書き直してみましょう。


// 1. 送信機能のルール(インターフェース)を決める
interface MessageService {
    send(message: string): void;
}

// 2. メール送信の具体的な中身を作る
class EmailService implements MessageService {
    send(message: string) {
        console.log("メールで通知: " + message);
    }
}

// 3. LINE送信の具体的な中身も作れる
class LineService implements MessageService {
    send(message: string) {
        console.log("LINEで通知: " + message);
    }
}

// 4. 通知クラス(中身を外から受け取るようにする)
class UserNotification {
    // コンストラクタで「MessageService」を受け取る(これがDI!)
    constructor(private service: MessageService) {}

    notify(message: string) {
        this.service.send(message);
    }
}

// 5. 使う時に好きなほうを「注入」する
const emailNotice = new UserNotification(new EmailService());
emailNotice.notify("メール版です");

const lineNotice = new UserNotification(new LineService());
lineNotice.notify("LINE版です");

このように、UserNotificationクラスは「何で送るか」を知らなくてもよくなりました。外側からEmailServiceLineServiceを「注入」することで、柔軟に対応できるようになったのです。

4. なぜES6/ESNextとの連携が重要なのか?

4. なぜES6/ESNextとの連携が重要なのか?
4. なぜES6/ESNextとの連携が重要なのか?

TypeScriptはES6(ECMAScript 2015)以降の標準仕様をベースにしています。今回紹介したclassconstructor、そしてモジュールの仕組みは、すべて現代のJavaScriptの標準に基づいています。

プログラミング未経験の方は、「なぜこんなに遠回りをするの?」と思うかもしれません。しかし、大規模なシステム開発では、この「部品化」が非常に重要です。例えば、本物のメールを飛ばすと料金がかかる場合、テストの時だけ「画面に文字を出すだけの偽物サービス」を注入すれば、お金をかけずに動作確認ができます。これをモック(Mock)と呼び、エンジニアの世界では日常的に使われるテクニックです。

5. パソコン初心者でもわかる!DIのメリット3選

5. パソコン初心者でもわかる!DIのメリット3選
5. パソコン初心者でもわかる!DIのメリット3選

DIを取り入れることで、プログラム作成がどのように楽になるかを整理します。

  1. 再利用性が高まる: 一度作ったクラスを、別の場所でも使い回しやすくなります。
  2. 修正が怖くない: 「メール」の機能を直しても、「通知」の仕組み全体を壊す心配が減ります。
  3. チーム開発がスムーズ: 「私はメール送信の部品を作るね」「じゃあ私は通知のガワを作るよ」といった具合に、分担して作業ができます。

パソコンのフォルダ分けと同じように、プログラムも整理整頓が大切です。DIは、コードという書類を適切なファイルに分類し、必要なときに必要なものを取り出せるようにする、魔法の整理術なのです。

6. 具体的な活用シーン:設定情報の注入

6. 具体的な活用シーン:設定情報の注入
6. 具体的な活用シーン:設定情報の注入

他にも、設定情報をDIで管理する例があります。例えば、開発用サーバーと本番用サーバーで接続先を切り替えたいときです。


interface AppConfig {
    apiUrl: string;
}

class ApiClient {
    constructor(private config: AppConfig) {}

    connect() {
        console.log(this.config.apiUrl + " に接続しています...");
    }
}

// 開発用の設定
const devConfig: AppConfig = { apiUrl: "http://localhost:3000" };
const devClient = new ApiClient(devConfig);
devClient.connect();

// 本番用の設定
const prodConfig: AppConfig = { apiUrl: "https://api.example.com" };
const prodClient = new ApiClient(prodConfig);
prodClient.connect();

http://localhost:3000 に接続しています...
https://api.example.com に接続しています...

このように、クラスを一切書き換えることなく、渡すデータ(設定)を変えるだけで挙動をコントロールできるのがDIの凄さです。これはTypeScriptとESNextのクラス機能を最大限に活かした設計と言えます。

7. まとめとしてのポイント

7. まとめとしてのポイント
7. まとめとしてのポイント

ここまで、TypeScriptにおけるDI(依存性の注入)について解説してきました。難しい用語も多かったかもしれませんが、大切なのは「クラスの中で新しい部品を作らず、外から渡してもらう」という一点に尽きます。

プログラミングの学習は、一歩ずつ進んでいけば必ず身につきます。まずは小さなクラスから、DIを意識して書いてみることから始めてみましょう。最初は面倒に感じるかもしれませんが、将来のあなたが「あの時DIにしておいてよかった!」と思う日が必ず来ます。

カテゴリの一覧へ
新着記事
New1
TypeScript
TypeScriptで非同期処理を含むES6構文をテストする方法!初心者のための徹底解説
New2
TypeScript
TypeScriptの関数に型をつける方法(引数・戻り値)を初心者向けに徹底解説!
New3
TypeScript
TypeScriptのアロー関数の書き方と使い分けを完全ガイド!初心者にもわかる関数定義の新しい形
New4
JavaScript
JavaScriptの正規表現フラグ(g, i, m)の意味と活用方法
人気記事
No.1
Java&Spring記事人気No1
JavaScript
JavaScriptのインストール方法まとめ!Windows・Mac・Linux別にステップ解説
No.2
Java&Spring記事人気No2
JavaScript
JavaScriptプログラムの実行方法まとめ!ブラウザ・Node.js・コンソールの使い方
No.3
Java&Spring記事人気No3
JavaScript
JavaScriptの日時操作Tips!実務で役立つテクニック集
No.4
Java&Spring記事人気No4
TypeScript
TypeScriptとES6/ESNextの連携ガイド!最新機能を有効にするtarget設定
No.5
Java&Spring記事人気No5
TypeScript
TypeScriptでコメントを書く正しい書き方と使い分け【初心者向けにやさしく解説】
No.6
Java&Spring記事人気No6
TypeScript
TypeScriptのif文の使い方を完全ガイド!初心者でもわかる条件分岐
No.7
Java&Spring記事人気No7
JavaScript
JavaScriptで日時の差分を計算する方法|ミリ秒・日数の応用テクニック
No.8
Java&Spring記事人気No8
JavaScript
JavaScriptの正規表現(RegExp)の基本構文と仕組みを解説