本文 AI 產出,尚未審核

TypeScript 課程 – 編譯與設定(Compiler Configuration)

主題:tsconfig.json 結構


簡介

在使用 TypeScript 開發任何規模的專案時,tsconfig.json 是唯一需要關注的檔案,它告訴編譯器(tsc)該如何把 .ts.tsx 轉換成 JavaScript。沒有正確的設定,程式碼可能會出現型別錯誤、編譯速度變慢,甚至在生產環境中產生不可預期的行為。
本單元將從 結構常見選項實務範例 逐層剖析 tsconfig.json,協助你在專案啟動或升級時,快速建立符合團隊需求的設定檔。


核心概念

1. 基本檔案結構

{
  "compilerOptions": { /* 編譯器選項 */ },
  "include": [ "src/**/*.ts" ],
  "exclude": [ "node_modules", "dist" ],
  "files": [ "global.d.ts" ]
}
欄位 作用 常見使用情境
compilerOptions 設定編譯器行為,如目標語言、模組系統等。 大多數專案的核心設定。
include 指定要編譯的檔案或目錄,支援 glob pattern。 僅編譯 src 資料夾。
exclude 排除不需要編譯的路徑,預設會排除 node_modules 避免編譯測試產出或第三方套件。
files 明確列出單一檔案,會覆寫 include/exclude 的行為。 加入全局宣告檔或特殊腳本。

2. compilerOptions 常用屬性

屬性 說明 範例
target 編譯後的 ECMAScript 版本。 (es5es6esnext) "target": "es2019"
module 產出模組系統。 (commonjsesnextamd) "module": "commonjs"
strict 開啟所有嚴格型別檢查。等同於啟用多個子選項。 "strict": true
noImplicitAny 禁止隱式 any "noImplicitAny": true
sourceMap 產生 .map 檔以支援除錯。 "sourceMap": true
outDir 指定輸出目錄。 "outDir": "./dist"
rootDir 設定根目錄,決定相對路徑的計算方式。 "rootDir": "./src"
paths & baseUrl 設定別名 import。 "baseUrl": "./", "paths": { "@utils/*": ["src/utils/*"] }
esModuleInterop 允許 import default 從 CommonJS 模組匯入。 "esModuleInterop": true
incremental 啟用增量編譯,加速大型專案。 "incremental": true
composite 用於 Project References,必須與 declaration 同時開啟。 "composite": true

3. 範例一:最小可用的 tsconfig.json

{
  "compilerOptions": {
    "target": "es6",
    "module": "commonjs",
    "strict": true,
    "outDir": "dist"
  },
  "include": ["src"]
}

說明:此設定適合小型 Node.js 專案,只需要把 src 目錄下的檔案編譯成 ES6、使用 CommonJS 模組,並將結果輸出至 dist


4. 範例二:使用別名與路徑映射

{
  "compilerOptions": {
    "target": "es2020",
    "module": "esnext",
    "baseUrl": ".",
    "paths": {
      "@components/*": ["src/components/*"],
      "@models": ["src/models/index.ts"]
    },
    "moduleResolution": "node",
    "esModuleInterop": true,
    "sourceMap": true,
    "outDir": "build"
  },
  "include": ["src/**/*.ts", "src/**/*.tsx"]
}
// src/main.ts
import { Button } from '@components/ui/Button';
import { User } from '@models';

console.log(Button, User);

重點:透過 baseUrl + paths,可以在程式碼中使用 簡潔的別名,避免長相對路徑 (../../../) 帶來的維護成本。


5. 範例三:增量編譯與 Project References(大型 monorepo)

// packages/api/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "declarationMap": true,
    "incremental": true,
    "outDir": "../../dist/api",
    "rootDir": "./src"
  },
  "include": ["src"],
  "references": [{ "path": "../shared" }]
}
// packages/shared/tsconfig.json
{
  "compilerOptions": {
    "composite": true,
    "declaration": true,
    "outDir": "../../dist/shared",
    "rootDir": "./src"
  },
  "include": ["src"]
}

說明composite 必須與 declaration 同時開啟,才能讓 Project References 正常運作。這樣的設定讓 api 專案在編譯時只重新編譯變更的 shared 部分,大幅縮短 CI 時間。


6. extends:共享基礎設定

在多個子專案需要相同的編譯規則時,可建立一個 共用基礎檔

// tsconfig.base.json
{
  "compilerOptions": {
    "target": "es2022",
    "module": "commonjs",
    "strict": true,
    "noImplicitAny": true,
    "esModuleInterop": true,
    "skipLibCheck": true
  }
}

子專案只需要 extends

// tsconfig.json (子專案)
{
  "extends": "../../tsconfig.base.json",
  "compilerOptions": {
    "outDir": "dist",
    "rootDir": "src"
  },
  "include": ["src"]
}

優點:統一風格、減少重複設定,且只要基礎檔改動,所有子專案即時受惠。


常見陷阱與最佳實踐

陷阱 可能的後果 解決方式
忘記排除 node_modules 編譯時間暴增,甚至因第三方 .d.ts 衝突導致錯誤。 exclude 中明確加入 "node_modules"(即使是預設)
include/excludefiles 同時使用 files 會覆寫 include/exclude,導致預期外的檔案未被編譯。 只使用一種:若需要全域宣告檔,將其放在 include 內或使用 typeRoots
過度依賴 any 失去型別安全,錯誤只能在執行時才被發現。 開啟 noImplicitAnystrictNullChecks,並使用 型別別名介面 替代
未設定 rootDir 輸出目錄結構可能與原始目錄不一致,造成路徑錯亂。 明確設定 rootDir,尤其在 monorepo 中更為重要
esModuleInterop 未開啟卻使用 import foo from "foo" 會出現 TS1192 錯誤。 開啟 esModuleInterop,或改用 import * as foo from "foo"
incrementalcomposite 混用不當 增量檔 (.tsbuildinfo) 可能被意外刪除,導致全量重編譯。 確保 .gitignore 中保留 .tsbuildinfo,或在 CI 中重新產生

最佳實踐

  1. 最小化 compilerOptions:僅開啟必要的嚴格模式,避免過度限制開發速度。
  2. 使用 extends 共享設定,讓團隊在不同子模組間保持一致性。
  3. 開啟 sourceMapinlineSources(若需要),提升除錯體驗。
  4. 在 CI 中加入 tsc --noEmit 以驗證型別正確性,避免因編譯錯誤部署。
  5. 定期檢視 tsconfig.json,隨著 TypeScript 版本升級,可能有新選項可提升效能或安全性。

實際應用場景

1. 前端 React 專案(Vite + TypeScript)

需求:支援 JSX、別名 @/components,且在開發模式下使用 esnext,生產模式則降級至 ES5。

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "jsx": "react-jsx",
    "baseUrl": ".",
    "paths": {
      "@/*": ["src/*"]
    },
    "strict": true,
    "noEmit": true
  },
  "include": ["src"]
}

說明noEmit 讓 Vite 直接負責轉譯,tsc 僅作型別檢查。

2. Node.js 後端 (NestJS)

需求:產出 CommonJS、使用 experimentalDecorators、自動生成 .d.ts

{
  "compilerOptions": {
    "target": "es2019",
    "module": "commonjs",
    "declaration": true,
    "sourceMap": true,
    "outDir": "dist",
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "strict": true
  },
  "include": ["src/**/*.ts"],
  "exclude": ["node_modules", "dist"]
}

3. 多套件 Monorepo(Lerna / Nx)

需求:每個套件共用基礎設定,並使用 Project References。

// 根目錄 tsconfig.json
{
  "files": [],
  "references": [
    { "path": "packages/shared" },
    { "path": "packages/api" },
    { "path": "packages/web" }
  ]
}

每個套件皆 extends 根目錄的 tsconfig.base.json,同時在 package.json 中加入 "tsc -b" 作為 build script。


總結

  • tsconfig.json 是 TypeScript 專案的「指揮中心」,正確的結構 能讓編譯快速、錯誤明確、程式碼可維護。
  • 透過 compilerOptionsinclude/excludeextendsreferences 等欄位,你可以細緻控制目標語言、模組系統、型別嚴格度以及多套件間的相依關係。
  • 常見的陷阱(如未排除 node_modules、過度使用 any)只要遵守 最佳實踐(開啟嚴格模式、使用別名、共享設定),即可在開發與 CI 流程中保持高品質。
  • 依照不同的 應用場景(前端框架、Node 後端、Monorepo),只要調整少數屬性,就能得到最適合的編譯行為。

掌握 tsconfig.json 的結構與設定,你將能在任何 TypeScript 專案中,快速建立 一致、可擴充、且具備良好型別安全 的開發環境。祝你編譯順利、程式碼無慮!