Skip to main content

第20章:TypeScriptビルドを速くする(“コンテナ内ビルド”のコツ)🧑‍💻⚡️📦

この章の狙いはシンプル👇 **「TSのビルドが走る場面でも、待ち時間を短くする」**ことです 😆✨


0) まず結論:速くする“3つの作戦”🏎️💨

  1. そもそもビルド工程を走らせない(レイヤキャッシュを最大化)🧱
  2. 走ってしまうなら、差分ビルドを効かせる(BuildKitのキャッシュマウント+tscのincremental)🧠
  3. 成果物を“きれいに分離”して軽くする(マルチステージでdistだけ運ぶ)🎒

※BuildKitは今どきのDockerでは標準ビルダー扱いです。(Docker Documentation) ※RUN --mount=type=cache でビルド中のキャッシュを“ビルドを跨いで”残せます。(Docker Documentation)


1) “TSビルドが遅い”の正体をざっくり理解👀🧩

TSビルド(tsc)の重さは、だいたいこの2つが支配します👇

  • 型チェックが重い(依存ライブラリの型も見る・プロジェクトが大きい等)🧠💦
  • 毎回フルビルドに戻ってしまう(差分情報が消える/残らない)🔁😵‍💫

ここで効くのが incrementalビルドincremental: true にすると、前回の情報を .tsbuildinfo に保存して次回を速くできます。(typescriptlang.org) さらに .tsbuildinfo の置き場は tsBuildInfoFile で好きに変えられます。(typescriptlang.org)


2) いちばん効く形:マルチステージ+distだけ運搬🏗️📦➡️🏃

「ビルド用」と「実行用」を分けて、最後はdistだけ持っていく形が王道です。(Docker Documentation)

✅ 推奨Dockerfile(TSビルド高速化セット)🧑‍🍳✨

ポイントはここ👇

  • npm ci等(依存導入)と tsc(ビルド)を別レイヤにしてキャッシュを守る🧱
  • tscBuildKitキャッシュマウント を与えて、incrementalの情報を残す🧠
  • 最終ステージは distだけ(軽い!速い!)🪶
## syntax=docker/dockerfile:1

FROM node:24 AS base
WORKDIR /app

## 依存だけ先に入れてキャッシュを最大化
FROM base AS deps
COPY package.json package-lock.json ./
## npmのDLキャッシュをBuildKitに残す(次回から速い)
RUN --mount=type=cache,target=/root/.npm \
npm ci

## ビルド
FROM base AS build
COPY --from=deps /app/node_modules ./node_modules
COPY tsconfig.json ./
COPY src ./src

## tscのincremental情報(.tsbuildinfoなど)をBuildKitキャッシュに残す
## 置き場は /app/.cache に集約する作戦
RUN --mount=type=cache,target=/app/.cache \
npm run build

## 実行用(distだけ運ぶ)
FROM node:24-slim AS runner
WORKDIR /app
ENV NODE_ENV=production

COPY package.json package-lock.json ./
RUN --mount=type=cache,target=/root/.npm \
npm ci --omit=dev

COPY --from=build /app/dist ./dist

CMD ["node", "dist/index.js"]

🧠 これで何が嬉しいの?

  • 依存が変わらない限り npm ci が再実行されにくい(キャッシュ大勝利)🏆
  • ソースが変わってビルドが走っても、/app/.cache が残るので 差分ビルドが効きやすい⚡️
  • 最終イメージは dist だけで軽い(起動も配布もラク)🎈

3) tsconfig側の“速くするスイッチ”🔧🧠

✅ まずはこれだけでOK(速さ優先の基本)⚡️

  • incremental: true …差分ビルドの要(typescriptlang.org)
  • tsBuildInfoFile …差分情報の置き場を固定(typescriptlang.org)
  • skipLibCheck: true …ライブラリ型チェックをスキップして速くする(精度は少し落ちる)(typescriptlang.org)
{
"compilerOptions": {
"outDir": "dist",
"incremental": true,
"tsBuildInfoFile": ".cache/tsbuildinfo",
"skipLibCheck": true
}
}

skipLibCheck は「速いけど、依存ライブラリの型の整合性チェックは弱くなる」やつです。 体感でビルドが軽くなることが多いので、まずONで試す価値ありです💨(typescriptlang.org)


4) “速くならない”あるある😇⚠️

あるある①:.tsbuildinfo が最終イメージに混ざる😵

outDir配下やdistと一緒に保存すると、最終ステージにコピーされがちです。 → だから .cache/tsbuildinfo に逃がすのが気持ちいいです🛟(typescriptlang.org)

あるある②:キャッシュマウントを使ってるつもりが効いてない🙃

RUN --mount=type=cacheBuildKit前提です。(Docker Documentation) (今どきは標準だけど、環境によっては無効化されてたりします)

あるある③:COPY . . が雑で毎回いろいろ壊す🧨

テスト・README・ローカル成果物などまで混ざると、ちょい変更でキャッシュ全崩壊します。 → COPY src ./src みたいに “必要な物だけCOPY” が強い💪✨


5) 🧪ミニ演習:ビルド時間が落ちるのを体感しよう⏱️📉

演習A:まず“分離”だけで速くする🏗️

  1. いまのDockerfileを上の「deps / build / runner」構成に寄せる

  2. 2回ビルドして、2回目がどれだけ速いか見る👀✨

    • 2回目が速い=レイヤキャッシュが働いてるサイン🧱

演習B:差分ビルドを効かせる⚡️

  1. tsconfigincrementaltsBuildInfoFile を入れる(typescriptlang.org)
  2. Dockerfileの npm run build--mount=type=cache,target=/app/.cache を付ける(Docker Documentation)
  3. src の小さい変更(1行)→ビルド
  4. もう1回別の小さい変更→ビルド → 2回目以降のビルドが軽くなるのを狙う🎯

演習C:skipLibCheckの効き具合を測る🏎️

  1. skipLibCheck: false でビルド
  2. skipLibCheck: true でビルド(typescriptlang.org) → 差が出たら「このプロジェクトは型チェックが重め」って分かる👍

6) 🤖AI活用:速くする“レビュー依頼テンプレ”3連発💬✨

① Dockerfileレビュー(キャッシュ目線)🧱

  • 「このDockerfileでキャッシュが壊れやすい行を3つ指摘して、直した案を出して」
  • 「依存導入(npm ci)とビルド(tsc)のレイヤを分ける最短構成にして」

② tsconfigレビュー(速度と安全のバランス)⚖️

  • 「ビルド速度が遅い。incremental / tsBuildInfoFile / skipLibCheck を使って、メリデメ込みで提案して」

③ “最終イメージが重い”問題の切り分け🪶

  • 「最終イメージに開発用ツールやキャッシュが混ざってないかチェックして、削る方針を箇条書きで」

7) まとめ:第20章チェックリスト✅✨

  • depsbuild を分けて、依存キャッシュを守った🧱
  • dist をマルチステージで運ぶ形にした📦(Docker Documentation)
  • incrementaltsBuildInfoFile で差分ビルドの土台を作った🧠(typescriptlang.org)
  • RUN --mount=type=cache.cache をビルド間で残した⚡️(Docker Documentation)
  • skipLibCheck を試して、速度差を計測した🏎️(typescriptlang.org)

次の第21章は、いよいよ **「ファイルが遅い(特にWindows×コンテナ)」**の現実編です 🪟🐢 ここで “体感が一段階” 変わりますよ…!🔥