TypeScript 基礎概念(Basics)
主題:tsconfig.json 設定
簡介
在使用 TypeScript 開發專案時,tsconfig.json 是整個編譯流程的指揮中心。它不僅決定了哪些檔案會被編譯、編譯結果的輸出位置,還能透過各種選項讓編譯器在開發階段即時捕捉潛在的錯誤。沒有妥善設定的 tsconfig.json,往往會導致「跑不通」或「執行時才出錯」的情況,降低開發效率,甚至增加維護成本。
本篇文章針對 初學者到中階開發者,從最基本的結構說起,逐步說明常見的編譯選項、路徑別名、嚴格模式等實務設定,並提供完整範例、常見陷阱與最佳實踐,幫助你快速掌握 tsconfig.json 的使用技巧,讓 TypeScript 在專案中發揮最大效益。
核心概念
1. tsconfig.json 的基本結構
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"strict": true,
"outDir": "./dist",
"rootDir": "./src"
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist"]
}
| 欄位 | 說明 |
|---|---|
compilerOptions |
編譯器的所有設定,幾乎所有可調整行為都在此定義。 |
include |
指定要納入編譯的檔案或目錄,支援 glob 模式。 |
exclude |
排除不需要編譯的檔案或目錄,預設會排除 node_modules。 |
files |
若只想編譯特定檔案,可直接列出檔名(會覆寫 include/exclude)。 |
小技巧:若
include、exclude、files都未設定,TypeScript 會自動搜尋專案根目錄下的所有*.ts、*.tsx檔案。
2. 常用編譯選項
2.1 target & module
{
"compilerOptions": {
"target": "ES2019", // 編譯成的 ECMAScript 版本
"module": "esnext" // 產出模組系統(commonjs、esnext、amd…)
}
}
target:決定產出的 JavaScript 語法等級,若設定ES5,則會把async/await轉譯為 Promise+generator。module:在 Node.js 環境常用commonjs,在前端建置(Webpack、Vite)則多選esnext讓打包工具自行處理。
2.2 strict 系列
{
"compilerOptions": {
"strict": true, // 開啟全部嚴格檢查
"noImplicitAny": true, // 隱式 any 會報錯
"strictNullChecks": true, // null/undefined 必須顯式處理
"noImplicitThis": true, // this 的類型必須明確
"alwaysStrict": true // 輸出 "use strict"
}
}
- 開啟
strict後,TypeScript 會在編譯階段即捕捉大多數常見錯誤,是提升程式品質的第一步。
2.3 esModuleInterop & allowSyntheticDefaultImports
{
"compilerOptions": {
"esModuleInterop": true,
"allowSyntheticDefaultImports": true
}
}
- 允許
import foo from "foo"直接匯入 CommonJS 模組,避免default與module.exports的衝突。
3. 路徑別名(Path Alias)與根目錄
在大型專案中,相對路徑 (../../../utils) 常讓人閱讀困難。透過 paths 與 baseUrl 可以建立簡潔的別名。
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@models/*": ["models/*"],
"@utils/*": ["utils/*"]
}
}
}
使用方式:
// src/services/userService.ts
import { User } from "@models/User";
import { formatDate } from "@utils/date";
export function getUserInfo(id: number) {
// ...
}
注意:若使用 Webpack、Vite 或 ts-node 等執行環境,別忘了同步設定對應的別名(
webpack.config.js、vite.config.ts、ts-node的-r tsconfig-paths/register等)。
4. 輸出設定:outDir、rootDir、declaration
{
"compilerOptions": {
"outDir": "./dist", // 編譯結果放置目錄
"rootDir": "./src", // 原始碼根目錄
"declaration": true, // 產生 .d.ts 型別宣告檔
"sourceMap": true, // 產生 .map 供除錯使用
"removeComments": false // 保留註解(可設定 true 移除)
}
}
declaration:若要將 TypeScript 套件發佈至 npm,建議開啟,讓使用者在 JavaScript 中也能得到型別提示。sourceMap:在瀏覽器或 Node.js 除錯時,能直接對照到原始的.ts檔案。
5. 進階選項:incremental、watch、skipLibCheck
| 選項 | 作用 | 建議情境 |
|---|---|---|
incremental |
啟用增量編譯,產生 .tsbuildinfo 檔,減少重建時間。 |
大型專案、CI/CD 中頻繁編譯。 |
watch |
讓編譯器持續監看檔案變化,等同 tsc -w。 |
開發階段本機快速迭代。 |
skipLibCheck |
跳過 .d.ts 檔的型別檢查,提升編譯速度。 |
第三方套件型別不穩定時使用。 |
程式碼範例
範例 1:最小化的 tsconfig.json(適合小型腳本)
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "dist"
},
"include": ["src/**/*.ts"]
}
只要設定
target、module、outDir,即可快速把src目錄下的 TypeScript 轉成 Node 可執行的 JavaScript。
範例 2:完整的前端專案設定(React + Vite)
{
"compilerOptions": {
"target": "ES2022",
"module": "esnext",
"jsx": "react-jsx",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"baseUrl": "./src",
"paths": {
"@components/*": ["components/*"],
"@hooks/*": ["hooks/*"]
},
"outDir": "./dist",
"declaration": false,
"sourceMap": true
},
"include": ["src"],
"exclude": ["node_modules"]
}
範例 3:打造可發佈的 npm 套件
{
"compilerOptions": {
"target": "ES2019",
"module": "commonjs",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"outDir": "lib",
"rootDir": "src",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true
},
"include": ["src"],
"exclude": ["tests", "node_modules"]
}
declaration+declarationMap讓使用者在 IDE 中同時得到.d.ts與對應的來源映射,提升套件可用性。
範例 4:使用增量編譯加速 CI
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"incremental": true,
"tsBuildInfoFile": "./.tsbuildinfo",
"outDir": "build",
"strict": true
},
"include": ["src"]
}
在 CI 中加入
--build參數(tsc -b),會自動利用.tsbuildinfo只編譯變更過的檔案,節省大量時間。
範例 5:結合 ts-node 與路徑別名的執行方式
{
"compilerOptions": {
"module": "commonjs",
"target": "ES2020",
"baseUrl": "./src",
"paths": {
"@app/*": ["*"]
}
},
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}
# 直接執行
npx ts-node -r tsconfig-paths/register src/index.ts
常見陷阱與最佳實踐
| 陷阱 | 可能原因 | 解決方式 / 最佳實踐 |
|---|---|---|
| 編譯後找不到模組 | paths 設定未同步至打包工具(Webpack/Vite) |
在相應的設定檔中也加入別名,或使用 tsconfig-paths 套件。 |
noImplicitAny 錯誤過多 |
第三方套件缺少型別定義 | 使用 --skipLibCheck 暫時忽略,或自行安裝 @types/...。 |
strictNullChecks 造成大量錯誤 |
現有程式碼未處理 null/undefined |
逐步開啟 strict,先從 strictNullChecks 開始,搭配 ! 斷言或 ?. 可緩解。 |
outDir 與 rootDir 設錯 |
產出檔案結構與原始碼不對應,導致路徑錯誤 | 確認 rootDir 指向原始碼根目錄,outDir 指向最終輸出位置,並檢查 include/exclude。 |
incremental 失效 |
tsconfig.json 被多次覆寫或 tsBuildInfoFile 被刪除 |
在 CI 中保留 .tsbuildinfo,或使用 -b(build mode)管理多個 tsconfig。 |
最佳實踐小結
- 先開
strict:即使會產生警告,也能在開發早期捕捉問題。 - 統一別名:
baseUrl+paths+ 打包工具同步設定,避免相對路徑過深。 - 產出
.d.ts:對外發佈套件或大型前端庫時,務必開啟declaration。 - 利用增量編譯:大型專案或 CI/CD 流程中加速編譯。
- 保持
include/exclude清晰:避免不必要的檔案被編譯,減少編譯時間。
實際應用場景
| 場景 | tsconfig.json 典型設定 |
為何這樣設定 |
|---|---|---|
| Node.js 後端服務 | target: ES2020、module: commonjs、outDir: ./dist、strict: true、esModuleInterop: true |
Node 目前支援 ES2020,使用 CommonJS 讓 require 正常,嚴格模式提升 API 安全性。 |
| React 前端專案(Vite) | target: ES2022、module: esnext、jsx: react-jsx、baseUrl: ./src、paths 別名、skipLibCheck: true |
Vite 依賴 ES 模組,react-jsx 為新版 JSX 轉譯,別名減少 import 雜訊。 |
| 共用 UI 元件庫(npm 套件) | declaration: true、outDir: ./lib、sourceMap: true、strict: true、esModuleInterop: true |
發佈給其他開發者時,需要 .d.ts、source map 方便除錯,嚴格模式保證元件 API 穩定。 |
| 微服務多專案 Monorepo | 每個子專案都有獨立 tsconfig.json,根目錄使用 references 連結 |
透過 Project References(references)實現跨套件型別檢查與增量編譯,提升整體建置效率。 |
| 測試環境(Jest + ts-jest) | module: commonjs、esModuleInterop: true、allowSyntheticDefaultImports: true、isolatedModules: true |
Jest 仍使用 CommonJS,isolatedModules 防止全域型別衝突,allowSyntheticDefaultImports 讓 import foo from 'foo' 正常運作。 |
總結
tsconfig.json 雖然看起來只是一個 JSON 檔,卻是 TypeScript 專案的*靈魂。*
- 透過
compilerOptions我們可以決定編譯目標、模組系統、嚴格檢查與輸出方式。 - 路徑別名、增量編譯、型別宣告檔 等進階功能,讓大型專案的維護與效能都能得到顯著提升。
- 常見的陷阱多與 設定不一致(例如別名未同步)或 過於寬鬆的型別檢查 有關,遵守最佳實踐即可減少問題。
在日常開發中,建議先以 最小可行的 tsconfig.json 起步,隨著需求逐步加入 strict、paths、incremental 等設定,最終形成符合團隊工作流程的完整配置。只要掌握上述概念與範例,你就能在任何規模的 TypeScript 專案中,快速、穩定地完成編譯與除錯,讓開發體驗更加順暢。祝你寫程式愉快!