TypeScript 教學:編譯與設定 – compilerOptions.sourceMap
簡介
在前端開發中,除錯是每日必做的工作。當我們使用 TypeScript 撰寫程式碼時,最終會被編譯成 JavaScript,若沒有適當的映射資訊,瀏覽器或 Node.js 只能顯示編譯後的檔案位置,讓除錯變得相當吃力。compilerOptions.sourceMap 正是為了解決這個問題而設計的,它會在編譯階段產生 .map 檔案,讓開發者在除錯工具(如 Chrome DevTools、VS Code)中仍能看到 原始的 TypeScript 程式碼,並直接在那裡設置斷點、觀察變數。
對於 初學者,source map 能讓學習曲線更平緩;對 中階開發者,則是提升除錯效率、減少定位錯誤時間的必備工具。本文將深入說明 sourceMap 的運作原理、設定方式、常見陷阱與最佳實踐,並提供多個實用範例,幫助你在專案中正確使用它。
核心概念
1. 什麼是 Source Map?
Source Map 本質上是一個 JSON 檔案,描述 編譯後的 JavaScript 行號 與 原始 TypeScript 行號 之間的對應關係。瀏覽器或 IDE 讀取這份檔案後,就能把錯誤堆疊(stack trace)或除錯資訊「還原」回原始檔案。
example.ts → tsc → example.js + example.js.map
example.js:編譯後的 JavaScript。example.js.map:對應的 Source Map,內含行列映射與原始檔案路徑。
2. 為什麼要開啟 sourceMap?
| 情境 | 若未開啟 sourceMap | 若開啟 sourceMap |
|---|---|---|
| 除錯 | 堆疊只顯示 .js 行號,難以定位 |
直接在 .ts 檔案中設斷點 |
| 錯誤報告 | 只能看到編譯後的代碼,使用者難以回報 | 錯誤訊息會指向原始檔案,回報更精確 |
| 開發者體驗 | 需要手動對照編譯前後代碼 | IDE 自動映射,省時省力 |
3. tsconfig.json 中的 sourceMap 設定
在 tsconfig.json 裡,只需要把 sourceMap 設為 true 即可:
{
"compilerOptions": {
"target": "ES2020",
"module": "commonjs",
"outDir": "./dist",
"sourceMap": true // <─ 開啟 source map
}
}
小技巧:若同時需要
.d.ts宣告檔,建議再加上"declaration": true,讓 IDE 同時取得型別與除錯資訊。
4. 與其他相關設定的關係
| 設定 | 影響範圍 | 與 sourceMap 的互動 |
|---|---|---|
inlineSourceMap |
把 map 直接寫入 .js 檔案的底部 |
互斥:若 true,sourceMap 會被忽略 |
sourceRoot |
指定 source map 中的根目錄 | 常配合 sourceMap 使用,讓路徑更簡潔 |
mapRoot |
指定 map 檔案的輸出目錄 | 讓產出結構更符合部署需求 |
removeComments |
移除編譯後的註解 | 不影響 map,但若需要保留原始註解可關閉 |
程式碼範例
以下示範 4 個常見情境,說明如何在不同環境下使用 sourceMap。
範例 1:最簡單的 tsconfig.json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2019",
"module": "esnext",
"outDir": "build",
"sourceMap": true // 開啟 source map
}
}
# 執行編譯
npx tsc
# 產出
# build/main.js
# build/main.js.map
說明:編譯後的
main.js.map會自動放在同目錄,Chrome DevTools 會自動載入。
範例 2:使用 inlineSourceMap(適合單檔測試)
{
"compilerOptions": {
"target": "ES2022",
"module": "commonjs",
"outDir": "dist",
"inlineSourceMap": true // 把 map 內嵌到 .js
}
}
// src/calc.ts
export function add(a: number, b: number): number {
return a + b; // <-- 想在此處除錯
}
npx tsc
# 產出 dist/calc.js,檔案底部會有
//# sourceMappingURL=data:application/json;base64,....
適用情境:快速測試或在 CI 中不希望產生額外檔案時使用。
範例 3:設定 sourceRoot 讓路徑更乾淨
{
"compilerOptions": {
"outDir": "lib",
"sourceMap": true,
"sourceRoot": "/src" // map 中的來源路徑會從 /src 開始
}
}
// src/utils/logger.ts
export const log = (msg: string) => console.log(`[LOG] ${msg}`);
編譯後的 logger.js.map 會包含:
{
"version":3,
"file":"logger.js",
"sourceRoot":"/src",
"sources":["utils/logger.ts"],
...
}
好處:在部署到 CDN 時,source map 仍能正確指向原始檔案,且不暴露本機絕對路徑。
範例 4:結合 webpack 與 ts-loader 的設定
webpack.config.js
const path = require('path');
module.exports = {
entry: './src/index.ts',
mode: 'development', // 開發模式自動產生 source map
devtool: 'source-map', // 告訴 webpack 產生外部 .map 檔
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
}
};
npm run build # 會產生 dist/bundle.js + dist/bundle.js.map
說明:即使
tsconfig.json沒有開sourceMap,只要webpack的devtool設為source-map,最終仍會得到完整的映射檔。
常見陷阱與最佳實踐
| 陷阱 | 現象 | 解決方案 |
|---|---|---|
忘記同步 sourceMap 與 inlineSourceMap |
同時開啟兩者會產生多餘檔案或無法正確載入 | 僅保留一個,根據需求選擇 true 或 inline |
產出目錄與 sourceRoot 不一致 |
DevTools 顯示 Cannot find source file |
確認 sourceRoot 指向正確的相對路徑,或直接省略 |
| 在生產環境洩漏原始程式碼 | 上傳的 .map 檔案被外部下載,暴露商業邏輯 |
於 CI/CD 階段移除 .map,或在伺服器設定僅允許內部存取 |
tsc 與其他編譯工具(如 Babel)產生衝突 |
兩套工具產生不同的 .map,導致混亂 |
統一使用同一套工具產生 map,或在 babel 中設定 sourceMaps: true 並關閉 tsc 的 sourceMap |
| 未在 IDE 中啟用「自動載入 Source Map」 | 雖有 .map,但除錯時仍顯示編譯後代碼 |
在 VS Code 中確認 javascript.debugger.autoAttach 為 on,或在 Chrome DevTools 開啟「Enable JavaScript source maps」 |
最佳實踐清單
- 開發環境:
sourceMap: true+sourceRoot(若需要) - 測試環境:可使用
inlineSourceMap減少檔案數量 - 生產環境:不要上傳
.map,或在 CDN 設定Access-Control-Allow-Origin為內部 IP - CI/CD:在建置腳本中加入
rm -rf *.map,確保不會意外洩漏 - 版本控制:
.map檔案 不 需要加入 Git,使用.gitignore排除
# .gitignore
/dist
/*.map
實際應用場景
1. 前端大型單頁應用(SPA)
在 React、Vue 或 Angular 專案中,每個元件 都會被 TypeScript 編譯成獨立的 JavaScript 檔。若沒有 source map,除錯時只能看到壓縮後的代碼,定位錯誤會變成「猜測」的過程。啟用 sourceMap 後,開發者可以直接在元件的 .tsx 或 .vue 檔案內設斷點,快速定位問題。
2. Node.js 後端服務
Node.js 在執行時會載入編譯後的 .js。使用 ts-node-dev 時,內建會產生暫時的 source map;若自行使用 tsc 打包成 Docker 映像檔,建議在 Dockerfile 中保留 *.map,以便在容器內部的遠端除錯(如 VS Code Remote - Containers)時使用。
# Dockerfile 範例
FROM node:20-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY tsconfig.json .
COPY src ./src
RUN npm run build # 產生 .js + .js.map
CMD ["node", "dist/index.js"]
3. 第三方函式庫發佈
若你打算把自製的 TypeScript 函式庫發佈至 npm,建議在 package.json 中同時提供 .d.ts 與 .js.map。使用者在除錯時能直接看到原始的 TypeScript 實作,提升套件的可維護性與開發者體驗。
// package.json
{
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*.js",
"dist/**/*.d.ts",
"dist/**/*.map"
]
}
總結
compilerOptions.sourceMap是 TypeScript 提供的除錯利器,讓編譯後的 JavaScript 能映射回原始的 TypeScript 檔案。- 正確的 設定(
sourceMap: true、sourceRoot、mapRoot)可以讓除錯工具即時顯示原始代碼,提升開發效率。 - 在 開發、測試與生產 三個階段,應依需求選擇
sourceMap、inlineSourceMap或完全關閉,以兼顧除錯便利與安全性。 - 常見的 陷阱 包括設定衝突、路徑不一致以及生產環境意外洩漏原始程式碼;遵循最佳實踐(如
.gitignore、CI/CD 移除 map)即可避免。 - 無論是 SPA 前端、Node.js 後端,或是 npm 套件,source map 都是提升開發者體驗與維護品質的關鍵工具。
把 sourceMap 加入你的 TypeScript 專案,讓每一次的除錯都變得 清晰、快速且安全!祝你寫程式寫得開心、除錯寫得順利 🚀.