T-CREATOR

TypeScript 共有可能な tsconfig 設計:`tsconfig/bases`で複数パッケージを一括最適化

TypeScript 共有可能な tsconfig 設計:`tsconfig/bases`で複数パッケージを一括最適化

複数の TypeScript パッケージを管理する際、各パッケージで似たような tsconfig.json を何度も書いた経験はありませんか?

モノレポやマルチパッケージ構成では、設定ファイルの重複やメンテナンス負荷が大きな課題となります。そこで注目されているのが @tsconfig​/​bases パッケージを活用した共有可能な設定設計です。

本記事では、TypeScript 設定の共有化から継承ベースの最適化まで、段階的に解説していきます。

背景

TypeScript プロジェクトが大規模化するにつれて、設定管理の複雑さが顕著になってきました。

複数パッケージでの設定重複問題

現代のフロントエンド開発では、一つのプロジェクトで複数のパッケージを管理することが一般的になっています。例えば、以下のような構成をよく見かけます。

typescript// packages/api/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}
typescript// packages/web/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "esnext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "jsx": "react-jsx",
    "moduleResolution": "node"
  }
}

上記のように、基本的な設定項目が各パッケージで重複してしまいます。これでは以下の問題が発生します。

プロジェクト全体の設定管理における主な課題を図で示します。

mermaidflowchart TD
    project[プロジェクトルート] --> api[API パッケージ]
    project --> web[Web パッケージ]
    project --> shared[共有ライブラリ]

    api --> api_config[独自 tsconfig.json]
    web --> web_config[独自 tsconfig.json]
    shared --> shared_config[独自 tsconfig.json]

    api_config --> duplicate1[重複設定]
    web_config --> duplicate2[重複設定]
    shared_config --> duplicate3[重複設定]

    duplicate1 --> maintenance_issue[メンテナンス負荷]
    duplicate2 --> maintenance_issue
    duplicate3 --> maintenance_issue

図で理解できる要点:

  • 各パッケージが独自の設定ファイルを持つ構造
  • 基本設定の重複による管理コスト増加
  • 設定変更時の影響範囲拡大

tsconfig.json メンテナンス課題

個別の tsconfig.json ファイルを管理することで生じる具体的な課題は以下の通りです。

設定の同期作業が手動になる TypeScript のバージョンアップ時や新機能の活用時に、全パッケージの設定を手動で更新する必要があります。これは時間がかかるだけでなく、更新漏れのリスクも伴います。

設定項目の意味理解が必要 各開発者が compilerOptions の詳細を理解していないと、適切な設定変更ができません。特に moduleResolutiontarget などの相互依存関係のある設定では、誤った組み合わせによるビルドエラーが発生することがあります。

プロジェクト固有設定との境界が曖昧 どの設定が全プロジェクト共通で、どれがパッケージ固有なのかが不明確になりがちです。これにより、本来共通化すべき設定が分散してしまいます。

課題

背景で述べた問題は、実際の開発現場で以下のような具体的な課題として現れています。

設定の一貫性保持の困難

複数のパッケージで異なる設定値が使われることで、予期しない動作が発生します。

型チェックの厳密さにばらつきが生じる あるパッケージでは strict: true を使用し、別のパッケージでは strict: false を使用している場合、同じコードでも型エラーの検出レベルが異なります。これにより、統合時に予期しない型エラーが発生することがあります。

typescript// Package A (strict: true)
function processUser(user: User | null) {
  return user.name; // エラー: Object is possibly 'null'
}

// Package B (strict: false)
function processUser(user: User | null) {
  return user.name; // エラーなし(実行時にランタイムエラーの可能性)
}

ターゲット環境の不整合 targetmodule の設定が異なることで、出力される JavaScript コードの互換性に問題が生じます。例えば、一部のパッケージが ES5 をターゲットにし、他が ES2020 をターゲットにしている場合、統合後のコードで動作環境の制約が不明確になります。

パッケージ間の設定差異によるトラブル

設定の違いが原因で発生する、デバッグが困難な問題について説明します。

パッケージ間での設定差異が引き起こす問題の流れを図で示します。

mermaidsequenceDiagram
    participant Dev as 開発者
    participant PkgA as パッケージA
    participant PkgB as パッケージB
    participant Build as ビルドシステム

    Dev->>PkgA: コード作成
    Dev->>PkgB: 同様のコード作成

    Note over PkgA: strict: true<br/>target: ES2020
    Note over PkgB: strict: false<br/>target: ES5

    PkgA->>Build: ビルド成功
    PkgB->>Build: ビルド成功

    Build->>Dev: 統合時にエラー

    Note over Dev: 設定差異の特定に<br/>時間を要する

図で理解できる要点:

  • 個別パッケージでは正常動作するコード
  • 統合時に発生する予期しないエラー
  • 設定差異の特定とデバッグの複雑さ

モジュール解決の不整合 moduleResolution の設定が異なると、同じ import 文でも異なるモジュールを参照する可能性があります。これは特にモノレポ環境で深刻な問題となります。

出力ディレクトリ構造のばらつき outDirrootDir の設定が統一されていない場合、ビルド後のファイル配置が予測しにくくなり、デプロイメント時に問題となることがあります。

設定変更時の影響範囲拡大

TypeScript 設定の変更が必要になった際の課題について詳しく見ていきましょう。

全パッケージへの変更適用コスト 新しい TypeScript 機能を活用するために設定変更が必要な場合、全てのパッケージで同じ変更を行う必要があります。パッケージ数が多いほど、この作業は煩雑になります。

例えば、useDefineForClassFields を有効にしたい場合:

typescript// 変更前(各パッケージで個別に設定)
// packages/api/tsconfig.json, packages/web/tsconfig.json, packages/shared/tsconfig.json...
{
  "compilerOptions": {
    // ... 他の設定
  }
}
typescript// 変更後(各パッケージで個別に追加)
{
  "compilerOptions": {
    // ... 他の設定
    "useDefineForClassFields": true
  }
}

設定ミスによる部分的な問題 手動での設定変更では、一部のパッケージで設定ミスや更新漏れが発生しやすくなります。これにより、プロジェクト全体で不整合が生じ、原因の特定に時間がかかります。

バージョン管理での追跡困難 複数ファイルでの同時変更は、Git の diff や Pull Request での変更内容把握を困難にします。レビュー時に設定変更の意図や影響範囲を正確に理解することが難しくなります。

解決策

これまで述べた課題を解決するための効果的なアプローチがあります。それが共有可能な tsconfig 設計です。

tsconfig/bases による共有設定

@tsconfig​/​bases パッケージは、よく使用される TypeScript 設定のプリセットを提供する Microsoft 公式のパッケージです。

@tsconfig/bases の仕組み このパッケージには、異なる実行環境やフレームワーク向けの最適化された設定が含まれています。

bash# パッケージのインストール
yarn add -D @tsconfig/node16 @tsconfig/react17

基本的な使用方法は以下のようになります:

typescript// packages/api/tsconfig.json
{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    // プロジェクト固有の設定のみを記述
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

この方法により、基本設定は @tsconfig​/​node16 から継承し、パッケージ固有の設定のみを個別に定義できます。

利用可能なプリセット一覧 主要な実行環境向けのプリセットが用意されています:

プリセット用途主な設定
@tsconfig​/​node16Node.js 16+ 環境target: ES2021, module: commonjs
@tsconfig​/​node18Node.js 18+ 環境target: ES2022, module: commonjs
@tsconfig​/​react17React 17 環境jsx: react-jsx, lib: DOM
@tsconfig​/​nextNext.js 環境Next.js 最適化設定
@tsconfig​/​viteVite 環境ESM とバンドル最適化

継承ベースの設計アプローチ

TypeScript の設定継承を活用して、段階的な設定管理を行います。

継承ベースの設定管理構造を図で示します。

mermaidflowchart TB
    base["@tsconfig/bases<br/>(公式プリセット)"] --> common[プロジェクト共通設定<br/>tsconfig.base.json]

    common --> node_base[Node.js 基本設定<br/>tsconfig.node.json]
    common --> web_base[Web 基本設定<br/>tsconfig.web.json]

    node_base --> api[API パッケージ<br/>packages/api/tsconfig.json]
    node_base --> worker[Worker パッケージ<br/>packages/worker/tsconfig.json]

    web_base --> frontend[Frontend パッケージ<br/>packages/web/tsconfig.json]
    web_base --> mobile[Mobile パッケージ<br/>packages/mobile/tsconfig.json]

    style base fill:#e1f5fe
    style common fill:#f3e5f5
    style node_base fill:#fff3e0
    style web_base fill:#fff3e0
    style api fill:#e8f5e8
    style worker fill:#e8f5e8
    style frontend fill:#e8f5e8
    style mobile fill:#e8f5e8

図で理解できる要点:

  • 公式プリセットからプロジェクト独自設定への段階的継承
  • 環境タイプ別の中間設定層による効率的な管理
  • 各パッケージは最小限の固有設定のみを持つ構造

段階的継承の実装例 プロジェクト共通設定から始まり、環境別、パッケージ別へと段階的に継承を行います:

typescript// tsconfig.base.json(プロジェクト共通設定)
{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true
  }
}
typescript// tsconfig.web.json(Web系共通設定)
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "jsx": "react-jsx",
    "lib": ["DOM", "DOM.Iterable"],
    "moduleResolution": "node"
  }
}

パッケージタイプ別最適化

異なる用途のパッケージに対して、それぞれに最適化された設定を用意します。

API サービス向け最適化 サーバーサイド処理に特化した設定を行います:

typescript// tsconfig.api.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2020",
    "lib": ["ES2020"],
    "types": ["node"],
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true
  }
}

React アプリケーション向け最適化 フロントエンド開発に最適化された設定を行います:

typescript// tsconfig.react.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "module": "esnext",
    "target": "ES2020",
    "lib": ["DOM", "DOM.Iterable", "ES2020"],
    "jsx": "react-jsx",
    "moduleResolution": "node",
    "allowSyntheticDefaultImports": true,
    "isolatedModules": true
  }
}

ライブラリパッケージ向け最適化 再利用可能なライブラリ作成に適した設定を行います:

typescript// tsconfig.library.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "module": "esnext",
    "target": "ES5",
    "lib": ["ES2015"],
    "declaration": true,
    "declarationMap": true,
    "outDir": "./dist",
    "rootDir": "./src"
  }
}

この段階的な設定継承により、以下のメリットを得られます:

  • 設定の一元管理: 共通設定の変更が全パッケージに自動で反映される
  • 環境別最適化: 各パッケージタイプに応じた最適な設定を維持
  • メンテナンス性向上: 設定変更時の作業範囲を大幅に削減
  • 設定意図の明確化: 継承関係により設定の目的と影響範囲が明確になる

具体例

理論から実践へと移り、実際のプロジェクトでの共有設定実装例を詳しく見ていきます。

基本的な共有設定の作成

まずは最もシンプルな共有設定から始めて、段階的に構築していきましょう。

Step 1: プロジェクト構造の準備

一般的なモノレポ構造を想定した設定を作成します:

bash# プロジェクト構造の確認
my-project/
├── packages/
│   ├── api/          # Node.js API サーバー
│   ├── web/          # React フロントエンド
│   └── shared/       # 共有ライブラリ
├── tsconfig.base.json    # プロジェクト基本設定
├── tsconfig.node.json    # Node.js 用設定
├── tsconfig.react.json   # React 用設定
└── package.json

Step 2: 基本設定ファイルの作成

プロジェクト全体で共通する基本設定を定義します:

typescript// tsconfig.base.json
{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    // 型チェック強化
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,

    // インポート解決
    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,

    // 出力設定
    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    // その他便利な設定
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  },
  "exclude": [
    "node_modules",
    "dist",
    "build",
    "**/*.test.ts",
    "**/*.spec.ts"
  ]
}

Step 3: 環境別設定の作成

Node.js 環境向けの設定を作成します:

typescript// tsconfig.node.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "module": "commonjs",
    "target": "ES2020",
    "lib": ["ES2020"],
    "types": ["node"],
    "outDir": "./dist",
    "rootDir": "./src",

    // Node.js 特有の設定
    "resolveJsonModule": true,
    "allowJs": false
  }
}

React 環境向けの設定を作成します:

typescript// tsconfig.react.json
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "module": "esnext",
    "target": "ES2020",
    "lib": ["DOM", "DOM.Iterable", "ES2020"],

    // React 特有の設定
    "jsx": "react-jsx",
    "isolatedModules": true,

    // バンドラー向け設定
    "noEmit": true,
    "allowJs": true,
    "resolveJsonModule": true
  }
}

Step 4: パッケージ固有設定の作成

各パッケージでは、環境別設定を継承し最小限の設定のみを行います:

typescript// packages/api/tsconfig.json
{
  "extends": "../../tsconfig.node.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src"
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "src/**/*.test.ts",
    "src/**/*.spec.ts"
  ]
}
typescript// packages/web/tsconfig.json
{
  "extends": "../../tsconfig.react.json",
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"],
      "@/components/*": ["./components/*"]
    }
  },
  "include": [
    "src/**/*",
    "public/**/*"
  ]
}
typescript// packages/shared/tsconfig.json
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "module": "esnext",
    "target": "ES2015",
    "outDir": "./dist",
    "rootDir": "./src",

    // ライブラリ向け設定
    "declaration": true,
    "declarationMap": true
  },
  "include": [
    "src/**/*"
  ]
}

Step 5: パッケージマネージャー設定

必要な依存関係をインストールします:

json// package.json
{
  "devDependencies": {
    "@tsconfig/node16": "^1.0.0",
    "typescript": "^4.9.0"
  }
}

これで基本的な共有設定の構築が完了しました。各パッケージは必要最小限の設定のみを持ち、共通設定は一元管理されています。

モノレポでの活用パターン

実際のモノレポ環境では、より複雑な要件に対応する必要があります。実用的なパターンを見ていきましょう。

大規模モノレポでの設定例

多数のパッケージを含む実際のプロジェクト構造を想定してみます:

bash# より大規模な構造例
enterprise-project/
├── config/
│   └── typescript/
│       ├── tsconfig.base.json
│       ├── tsconfig.node.json
│       ├── tsconfig.react.json
│       ├── tsconfig.nextjs.json
│       └── tsconfig.library.json
├── packages/
│   ├── backend/
│   │   ├── api-server/         # Express API
│   │   ├── worker-service/     # バックグラウンドワーカー
│   │   └── auth-service/       # 認証サービス
│   ├── frontend/
│   │   ├── admin-panel/        # 管理画面 (React)
│   │   ├── user-app/           # ユーザー向けアプリ (Next.js)
│   │   └── mobile-app/         # モバイルアプリ (React Native)
│   └── shared/
│       ├── ui-components/      # UI コンポーネント
│       ├── utils/              # ユーティリティ
│       └── types/              # 型定義
└── tools/
    └── build-scripts/          # ビルドスクリプト

設定ファイルの詳細実装

各環境に特化した設定の詳細を見てみましょう:

typescript// config/typescript/tsconfig.base.json
{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "exactOptionalPropertyTypes": true,
    "noImplicitReturns": true,
    "noFallthroughCasesInSwitch": true,

    "moduleResolution": "node",
    "esModuleInterop": true,
    "allowSyntheticDefaultImports": true,
    "resolveJsonModule": true,

    "declaration": true,
    "declarationMap": true,
    "sourceMap": true,

    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,

    // パス解決設定(モノレポ用)
    "baseUrl": ".",
    "paths": {
      "@shared/ui/*": ["packages/shared/ui-components/src/*"],
      "@shared/utils/*": ["packages/shared/utils/src/*"],
      "@shared/types/*": ["packages/shared/types/src/*"]
    }
  }
}
typescript// config/typescript/tsconfig.nextjs.json
{
  "extends": "./tsconfig.react.json",
  "compilerOptions": {
    "target": "ES2017",
    "lib": ["DOM", "DOM.Iterable", "ES6"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "forceConsistentCasingInFileNames": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",

    // Next.js 特有の設定
    "incremental": true,
    "plugins": [
      {
        "name": "next"
      }
    ]
  }
}

各パッケージでの設定適用例

バックエンドサービスの設定:

typescript// packages/backend/api-server/tsconfig.json
{
  "extends": "../../../config/typescript/tsconfig.node.json",
  "compilerOptions": {
    "outDir": "./dist",
    "rootDir": "./src",
    "baseUrl": "./src",
    "paths": {
      "@/*": ["./*"],
      "@shared/*": ["../../../packages/shared/*/src"]
    }
  },
  "include": [
    "src/**/*"
  ],
  "exclude": [
    "src/**/*.test.ts",
    "src/**/*.spec.ts",
    "dist"
  ]
}

Next.js アプリケーションの設定:

typescript// packages/frontend/user-app/tsconfig.json
{
  "extends": "../../../config/typescript/tsconfig.nextjs.json",
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"],
      "@/components/*": ["./src/components/*"],
      "@shared/*": ["../../shared/*/src/*"]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts"
  ],
  "exclude": [
    "node_modules"
  ]
}

ワークスペース設定の連携

Yarn ワークスペースと連携した設定管理:

json// package.json (ルート)
{
  "name": "enterprise-project",
  "private": true,
  "workspaces": [
    "packages/backend/*",
    "packages/frontend/*",
    "packages/shared/*"
  ],
  "devDependencies": {
    "@tsconfig/node16": "^1.0.0",
    "@tsconfig/react17": "^1.0.0",
    "typescript": "^4.9.0"
  },
  "scripts": {
    "type-check": "yarn workspaces foreach -A run type-check",
    "type-check:watch": "yarn workspaces foreach -A -j unlimited run type-check:watch"
  }
}

各パッケージで共通のスクリプトを定義:

json// packages/backend/api-server/package.json
{
  "name": "@enterprise/api-server",
  "scripts": {
    "type-check": "tsc --noEmit",
    "type-check:watch": "tsc --noEmit --watch",
    "build": "tsc",
    "dev": "ts-node-dev src/index.ts"
  }
}

モノレポでの活用における詳細な設定管理のフローを図で示します。

mermaidflowchart TD
    workspace[Yarn/NPM ワークスペース] --> config_dir[config/typescript/ 設定ディレクトリ]

    config_dir --> base_config[tsconfig.base.json<br/>共通基本設定]
    config_dir --> env_configs[環境別設定ファイル群]

    env_configs --> node_config[tsconfig.node.json]
    env_configs --> react_config[tsconfig.react.json]
    env_configs --> nextjs_config[tsconfig.nextjs.json]
    env_configs --> lib_config[tsconfig.library.json]

    node_config --> backend_packages[Backend パッケージ群]
    react_config --> react_packages[React パッケージ群]
    nextjs_config --> nextjs_packages[Next.js パッケージ群]
    lib_config --> shared_packages[共有ライブラリ群]

    backend_packages --> api[API Server]
    backend_packages --> worker[Worker Service]
    backend_packages --> auth[Auth Service]

    react_packages --> admin[Admin Panel]
    react_packages --> mobile[Mobile App]

    nextjs_packages --> user_app[User App]

    shared_packages --> ui_lib[UI Components]
    shared_packages --> utils_lib[Utils Library]
    shared_packages --> types_lib[Types Library]

    style workspace fill:#e1f5fe
    style config_dir fill:#f3e5f5
    style base_config fill:#fff3e0
    style env_configs fill:#fff3e0

図で理解できる要点:

  • ワークスペースレベルでの一元的な設定管理
  • 設定ディレクトリによる整理された構造
  • パッケージタイプごとの最適化された継承チェーン

環境別設定の継承設計

開発・テスト・本番環境それぞれに最適化された設定を継承チェーンで管理する方法を説明します。

環境別設定の必要性

実際のプロジェクトでは、環境によって異なる TypeScript 設定が必要になることがあります:

  • 開発環境: 高速なコンパイル、詳細なエラー情報、ソースマップ
  • テスト環境: テストフレームワークとの互換性、モック対応
  • 本番環境: 最適化されたビルド、最小限の出力ファイル

段階的継承による環境設定

環境別の設定を段階的に継承することで、効率的な管理を実現できます:

typescript// tsconfig.base.json(全環境共通)
{
  "extends": "@tsconfig/node16/tsconfig.json",
  "compilerOptions": {
    "strict": true,
    "noUncheckedIndexedAccess": true,
    "moduleResolution": "node",
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true
  }
}
typescript// tsconfig.development.json(開発環境)
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "sourceMap": true,
    "inlineSourceMap": false,
    "declaration": true,
    "declarationMap": true,

    // 開発効率向上のための設定
    "incremental": true,
    "tsBuildInfoFile": "./node_modules/.cache/typescript/tsconfig.tsbuildinfo",

    // 詳細なエラー情報
    "noErrorTruncation": true,
    "preserveWatchOutput": true
  }
}
typescript// tsconfig.test.json(テスト環境)
{
  "extends": "./tsconfig.development.json",
  "compilerOptions": {
    "types": ["node", "jest", "@testing-library/jest-dom"],

    // テスト用設定
    "allowJs": true,
    "esModuleInterop": true,
    "isolatedModules": false,

    // テスト環境用のパス解決
    "paths": {
      "@/*": ["./src/*"],
      "@test/*": ["./test/*"],
      "@mocks/*": ["./test/mocks/*"]
    }
  },
  "include": [
    "src/**/*",
    "test/**/*",
    "**/*.test.ts",
    "**/*.spec.ts"
  ]
}
typescript// tsconfig.production.json(本番環境)
{
  "extends": "./tsconfig.base.json",
  "compilerOptions": {
    "sourceMap": false,
    "declaration": false,
    "declarationMap": false,

    // 本番最適化設定
    "removeComments": true,
    "noEmitOnError": true,

    // 出力最適化
    "target": "ES2018",
    "module": "commonjs"
  },
  "exclude": [
    "**/*.test.ts",
    "**/*.spec.ts",
    "test/**/*",
    "**/*.stories.ts"
  ]
}

ビルドスクリプトとの連携

異なる環境設定を使い分けるためのスクリプト例:

json// package.json
{
  "scripts": {
    "dev": "ts-node-dev --project tsconfig.development.json src/index.ts",
    "build": "tsc --project tsconfig.production.json",
    "build:dev": "tsc --project tsconfig.development.json",
    "test": "jest --config jest.config.js",
    "type-check": "tsc --project tsconfig.test.json --noEmit",
    "type-check:prod": "tsc --project tsconfig.production.json --noEmit"
  }
}

Docker 環境との統合

コンテナ環境で環境別設定を活用する例:

dockerfile# Dockerfile.development
FROM node:16-alpine

WORKDIR /app
COPY package*.json ./
COPY yarn.lock ./

RUN yarn install

COPY . .

# 開発環境用のTypeScript設定でビルド
RUN yarn build:dev

CMD ["yarn", "dev"]
dockerfile# Dockerfile.production
FROM node:16-alpine

WORKDIR /app
COPY package*.json ./
COPY yarn.lock ./

# 本番用依存関係のみインストール
RUN yarn install --frozen-lockfile --production=false
COPY . .

# 本番環境用設定でビルド
RUN yarn build

# 不要なファイルを削除してイメージサイズを削減
RUN yarn install --production && yarn cache clean
RUN rm -rf src/ test/ *.config.js tsconfig.*.json

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

CI/CD パイプラインでの活用

GitHub Actions での環境別設定使用例:

yaml# .github/workflows/ci.yml
name: CI/CD Pipeline

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3
        with:
          node-version: '16'

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Type check (test environment)
        run: yarn type-check

      - name: Run tests
        run: yarn test

  build-production:
    runs-on: ubuntu-latest
    needs: test
    if: github.ref == 'refs/heads/main'
    steps:
      - uses: actions/checkout@v3
      - uses: actions/setup-node@v3

      - name: Install dependencies
        run: yarn install --frozen-lockfile

      - name: Type check (production)
        run: yarn type-check:prod

      - name: Build for production
        run: yarn build

      - name: Upload build artifacts
        uses: actions/upload-artifact@v3
        with:
          name: production-build
          path: dist/

環境別設定の継承関係と利用場面を図で示します。

mermaidstateDiagram-v2
    [*] --> BaseConfig: プロジェクト基本設定

    BaseConfig --> DevConfig: 開発環境設定
    BaseConfig --> ProdConfig: 本番環境設定

    DevConfig --> TestConfig: テスト環境設定

    DevConfig --> DevBuild: 開発ビルド
    TestConfig --> TestExecution: テスト実行
    ProdConfig --> ProdBuild: 本番ビルド

    DevBuild --> LocalDev: ローカル開発
    TestExecution --> CI_CD: CI/CD パイプライン
    ProdBuild --> Deployment: デプロイメント

    note right of BaseConfig
        strict: true
        共通型チェック設定
    end note

    note right of DevConfig
        sourceMap: true
        incremental: true
        開発効率重視
    end note

    note right of TestConfig
        jest types included
        test paths configured
    end note

    note right of ProdConfig
        removeComments: true
        最適化設定
    end note

図で理解できる要点:

  • 基本設定から環境別設定への段階的継承
  • 各環境の目的に応じた最適化設定
  • 開発フロー全体での設定活用パターン

まとめ

本記事では、TypeScript プロジェクトにおける設定管理の課題から、@tsconfig​/​bases を活用した共有可能な設定設計まで詳しく解説してきました。

実現できる主なメリット

設定管理の効率化 従来の個別設定管理から共有ベース設定への移行により、メンテナンス作業を大幅に削減できます。TypeScript 設定の変更が必要な場合も、基本設定ファイルの更新だけで全パッケージに反映されるため、作業時間の短縮とミスの軽減が実現できます。

プロジェクト全体での一貫性確保 継承ベースの設計により、全パッケージで一貫した型チェックレベルやコンパイル設定を維持できます。これにより、パッケージ間での統合時に発生する予期しないエラーを防ぐことができます。

環境別最適化の実現 開発・テスト・本番環境それぞれに最適化された設定を、継承チェーンで効率的に管理できます。環境固有の要件に応じてカスタマイズしながら、共通設定は一元管理されます。

チーム開発での生産性向上 設定の意図や継承関係が明確になることで、新しいチームメンバーでも設定を理解しやすくなります。また、設定変更時の影響範囲が予測しやすく、安全な変更が可能になります。

導入時の推奨アプローチ

段階的な導入 既存のプロジェクトに導入する場合は、まず一つのパッケージで共有設定を試験導入し、効果を確認してから他のパッケージに展開することを推奨します。

チーム内での合意形成 設定変更はチーム全体に影響するため、導入前にチームメンバーと設計方針について合意を取ることが重要です。継承関係や命名規則について統一見解を持つことで、より効果的な活用が可能になります。

継続的な改善 プロジェクトの成長に応じて設定も進化させていく必要があります。定期的に設定の見直しを行い、新しい TypeScript 機能や@tsconfig​/​basesの更新に合わせて最適化を継続しましょう。

今後の活用に向けて

共有可能な tsconfig 設計は、プロジェクトの規模が大きくなるほどその効果を発揮します。モノレポ環境での活用はもちろん、複数のプロジェクト間でも設定パターンを共有することで、組織全体での TypeScript 活用レベルを向上させることができます。

TypeScript エコシステムは今後も進化し続けるため、共有設定アプローチを採用することで、新機能への対応やベストプラクティスの適用もより容易になるでしょう。

本記事で紹介した設計パターンを参考に、あなたのプロジェクトに最適な共有設定を構築してみてください。効率的な設定管理により、より生産的な TypeScript 開発を実現できるはずです。

関連リンク