Dockerで異なるコンテナ間でTypescript型定義ファイルを共有する方法

7372 ワード

前書き

クライアントもサーバーもNode.jsに揃えた!もう同じ型定義ファイルを参照して書いていくだけ!

幸せな夢ですよね。しかし、docker環境での用例が中々見当たらず、試行錯誤を繰り返しました。

本記事で追求したかったもの

以下のポイントを追求しました。

  • dockerならではのコンテナ間の独立性を維持する
  • コンテナたちは親子関係ではなく、対等な関係で共有ファイルを参照したい
  • プロジェクトのルートは汚さない
  • 知らない人が見てわかりやすい(主観)

構造

この記事ではこのような階層を持ったプロジェクトを設定していく例で記事を書きます。
階層が異なる場合は適宜変更してください。

Project Root
 ┣━ client  クライアントのコンテナ
    ┣━ ...構成ファイル
    ┗━ tsconfig.json
 ┣━ server  サーバーのコンテナ
    ┗━ src
        ┣━ ...構成ファイル
        ┗━ tsconfig.json
 ┣━ types   共用で使う型定義ファイルを置くところ
    ┗━ entities.d.ts
 ┗━ docker-compose.yml
    ~~~~~~~~~~~~~~~~~~~~

docker-compose.ymlの設定 (全文)

⚠️ 本記事と関連のない箇所は省略中

services:
  client:
    volumes: #パス設定の仕方は下記参照
     - ./client:/usr/app
     - ./types:/usr/types #共通型定義ファイル
  # ...その他設定
  
  server:
    volumes: #パス設定の仕方は下記参照
      - ./server:/usr/src/app
      - ./types:/usr/types #共通型定義ファイル
  # ...その他設定

以下でそれぞれ説明します。

typesvolume設定

後術するtsconfig.jsonで、tsconfig.jsonからtypesフォルダまでの経路(相対パス)を書きます。
この経路で両方の環境から参照されるので、一個のパスを使ってホスト(IDE)とコンテナ、どっちからでも辿り着けるように設定しなければいけません。

Volumeの階層をホストの階層に合わせる

ホスト側の階層はいじらずに、コンテナ側の階層関係をホスト側と同じになるように設定します。
やりかたは、ホストでtsconfig.json=>typesの相対パスを求めたあと、
コンテナ上でtypesフォルダがどこにあれば、同じ相対パスで参照できるか考えればいいです。
一個一個見て行きましょう。

Client

  • ホストでのtsconfig.json=>types../types
  • コンテナでのtsconfig.jsonの場所は/usr/app/tsconfig.json
  • /usr/app/tsconfig.jonsから../移動すると/usr/になる
  • typesフォルダは/usr/typesにあればいい
ClientのVolume設定
  client:
    volumes: #パス設定の仕方は下記参照
     - ./client:/usr/app
     - ./types:/usr/types # <= ここ

Server

  • ホストでのtsconfig.json=>types : ../../types
  • コンテナでのtsconfig.jsonの場所は/usr/app/src/tsconfig.json
  • /usr/app/src/tsconfig.jsonから../../移動すると/usr/になる
  • typesフォルダは/usr/typesにあればいい
ServerのVolume設定
  server:
    volumes: #パス設定の仕方は下記参照
      - ./server:/usr/src/app
      - ./types:/usr/types  # <= ここ

それぞれのコンテナのtsconfig.json設定

compilerOptions内のpathsに登録しておきます。
上で計算したパスを入れていくだけです。
設定を済ませたtsconfig.json管轄内のtsファイルではimportができるようになります。

ファイルを1つ指定して使う場合

{
  "compilerOptions": {
    "paths": {
      "@entities": [ # 好きなように命名
        "../types/entities.d.ts" 
      ]
    }
  }
}

Serverコンテナの例では"../../types/entities.d.ts"

使い方

import { UserEntity } from "@entites" //ファイルを指定しているので、単一ファイルになります。

フォルダを指定して使う場合

{
  "compilerOptions": {
    "paths": {
      "@shared/*": [ # フォルダなので適切な命名
        "../types/*.d.ts"
      ]
    }
  }
}

Serverコンテナの例では"../../types/*.d.ts"

使い方

import { UserEntity } from "@shared/entities"

おかしいときにみるところ

コンテナのパスがおかしければビルドが失敗します。
ホストのパスがおかしければIDE上でエラーが出る!