本文 AI 產出,尚未審核
TypeScript 編譯與設定:include / exclude 完全攻略
簡介
在使用 TypeScript 時,最常見的問題之一就是 編譯範圍 設定不當,導致不必要的檔案被編譯、或是重要的程式碼被忽略。tsconfig.json 中的 include 與 exclude 欄位正是用來控制 哪些檔案會被 TypeScript 編譯器 (tsc) 處理的核心機制。
掌握這兩個屬性的使用方法,不只可以縮短編譯時間、減少產生的錯誤訊息,還能讓專案結構更清晰、維護成本下降。本文將從概念說明、實作範例、常見陷阱與最佳實踐,一步步帶你建立正確的編譯設定。
核心概念
1. include 的基本原理
include接受一個 字串陣列,每個字串都是 Glob pattern(類似 UNIX 的檔案匹配規則)。- 若未指定
include,預設會 包含tsconfig.json所在目錄下的 所有.ts、.tsx、.d.ts檔案(不含node_modules)。 include只 定義 要加入 的檔案,之後還會受到exclude的過濾。
範例 1:只編譯 src 目錄下的程式碼
{
"compilerOptions": { "outDir": "dist" },
"include": ["src/**/*.ts"]
}
這裡的
src/**/*.ts表示「src資料夾內,任意子目錄的所有.ts檔」。
2. exclude 的基本原理
exclude同樣接受 Glob pattern 陣列,用來 排除 不想編譯的檔案或目錄。- 預設的排除項目已包含
node_modules、bower_components、jspm_packages。 exclude的篩選會在include之後執行,也就是說先加入再排除。
範例 2:排除測試檔與產出目錄
{
"compilerOptions": { "outDir": "dist" },
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.spec.ts", "dist"]
}
src/**/*.spec.ts為測試檔,dist為編譯後的輸出目錄,兩者都不需要再次被編譯。
3. files 與 include / exclude 的差異
files是 顯式列出 必須編譯的檔案,不會 受exclude影響。- 若同時使用
files與include,最終編譯的檔案集合是 兩者的聯集(union),再經過exclude篩選。 - 建議 盡量避免 同時使用
files與include,以免產生混淆。
範例 3:混用 files 與 include(不推薦)
{
"compilerOptions": { "outDir": "dist" },
"files": ["src/main.ts"],
"include": ["src/**/*.ts"],
"exclude": ["src/legacy/**/*.ts"]
}
這樣會先把
src/main.ts與src/**/*.ts合併,再排除src/legacy/**/*.ts。若main.ts位於legacy目錄,仍會被編譯,造成意外。
4. 進階 Glob 語法
| Pattern | 代表意義 |
|---|---|
* |
匹配除 / 之外的任意字元(單層) |
** |
匹配任意深度的子目錄 |
? |
匹配單一字元 |
[ab] |
匹配方括號內的任意字元 |
! |
於 exclude 中可用於「排除」特定模式(例如 !src/**/test.ts) |
範例 4:同時包含 .ts 與 .tsx,但排除 node_modules 內的檔案
{
"compilerOptions": { "jsx": "react" },
"include": ["src/**/*.{ts,tsx}"],
"exclude": ["node_modules"]
}
5. extends 與基礎設定的結合
在大型專案中,常會把共用的編譯設定抽成 base tsconfig,再由子專案 extends。此時 include / exclude 會 覆寫(override)基礎設定,而不是合併。
範例 5:子專案覆寫 exclude
// tsconfig.base.json
{
"compilerOptions": { "target": "es2020", "module": "commonjs" },
"exclude": ["node_modules"]
}
// tsconfig.json (子專案)
{
"extends": "./tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"] // 只排除測試檔,保留基礎的 node_modules 排除
}
常見陷阱與最佳實踐
| 陷阱 | 說明 | 解決方式 |
|---|---|---|
忘記排除 dist 或 build |
編譯輸出目錄若未排除,會形成 無限遞迴(編譯器會把已編譯的 .js 再當作輸入) |
在 exclude 中加入 ["dist", "build"] |
使用過度寬鬆的 include |
include: ["**/*"] 會把根目錄下的所有檔案都帶入編譯,浪費時間 |
限定在實際的程式碼根目錄,如 src/**/*.ts |
同時使用 files 與 include |
兩者的結合容易產生「不小心編譯了」的檔案 | 盡量只使用一種,若需要精確控制,直接使用 files |
忽略預設的 node_modules 排除 |
手動指定 include 後,預設排除會失效,可能導致第三方套件被編譯 |
明確在 exclude 加入 node_modules,或使用 skipLibCheck |
| Glob 語法錯誤 | src/*/*.ts 只匹配兩層子目錄,深層檔案會被遺漏 |
使用 ** 代表任意深度,或測試 pattern 是否正確 |
最佳實踐
- 先寫
include再寫exclude:先確定要編譯的範圍,再排除例外。 - 保持
exclude包含輸出目錄:避免編譯產出再次被編譯。 - 使用
extends共享設定:大型 monorepo 可把共用exclude放在 base config。 - 利用
exclude的!反向模式(TS 4.5 以上)精細控制。 - 在 CI 中驗證
tsconfig.json:加入一步tsc --noEmit,確保設定不會意外漏編檔案。
實際應用場景
場景一:前端 React 專案 + Jest 測試
{
"compilerOptions": {
"target": "es2022",
"module": "esnext",
"jsx": "react-jsx",
"outDir": "dist"
},
"include": ["src/**/*.tsx", "src/**/*.ts"],
"exclude": [
"node_modules",
"dist",
"**/*.spec.ts",
"**/*.spec.tsx"
]
}
- 目的:只編譯實際的 React 元件與業務程式碼,測試檔 (
*.spec.*) 與產出目錄均排除。
場景二:Node.js 微服務 + Shared Library
{
"compilerOptions": {
"module": "commonjs",
"target": "es2019",
"outDir": "build",
"rootDir": "src"
},
"include": ["src/**/*.ts", "libs/**/*.ts"],
"exclude": ["src/**/*.test.ts", "build"]
}
- 目的:
src為服務程式碼,libs為共用函式庫,兩者皆編譯;測試檔與編譯產出排除。
場景三:Monorepo 多套件,使用基礎設定
// packages/core/tsconfig.json
{
"extends": "../../tsconfig.base.json",
"include": ["src/**/*.ts"],
"exclude": ["src/**/*.test.ts"]
}
- 目的:所有子套件共用
tsconfig.base.json的compilerOptions與exclude(如node_modules),僅在子套件自行加入測試檔排除。
總結
include與exclude是 控制 TypeScript 編譯範圍 的關鍵,合理配置可大幅提升編譯效率與專案可讀性。- 先 明確指定要編譯的目錄或檔案(
include),再 排除不需要的例外(exclude),避免不必要的檔案被拉入編譯流程。 - 注意 預設排除(
node_modules)會在自行設定include後失效,必須手動補回。 - 在大型或多套件專案中,善用
extends共享基礎設定,讓每個子專案只關心自己的include / exclude。 - 最後,持續在 CI 中驗證
tsconfig.json,確保設定不會因新增檔案而產生意外的編譯行為。
掌握了 include / exclude 的使用技巧,你就能在任何規模的 TypeScript 專案中,建立乾淨、快速、可預測的編譯流程。祝開發順利!