本文 AI 產出,尚未審核

TypeScript 基礎概念:編譯與轉譯(Transpiling to JS)


簡介

在前端開發的生態系統裡,TypeScript 已成為許多大型專案的首選語言。它在 JavaScript 的基礎上加入了靜態型別、介面、列舉等語法,讓程式碼更易維護、錯誤更早被捕捉。然而,瀏覽器只能直接執行 JavaScript,因此 TypeScript 必須先 編譯(transpile) 成純 JavaScript,才能在瀏覽器或 Node.js 環境中運行。

了解編譯與轉譯的流程不只是「把 .ts 檔案變成 .js」那麼簡單,還涉及 編譯設定(tsconfig.json)、目標語法版本(ES5、ES6…)、模組系統(CommonJS、ESM)以及 source map型別檢查 等重要概念。掌握這些基礎,才能在實務開發中避免不必要的錯誤、提升建置效率。


核心概念

1. 為什麼需要轉譯?

  • 瀏覽器相容性:舊版瀏覽器只支援 ES5,若直接寫 ES6+(例如 letasync/await),必須透過轉譯降級才能執行。
  • 型別安全:TypeScript 會在編譯階段檢查型別,若有錯誤會直接在編譯時期阻止產出 JS,避免執行時的未預期行為。
  • 開發體驗:利用編譯器的 增量編譯(incremental compilation)與 watch mode,即時看到錯誤並自動產生最新的 JavaScript 檔案。

2. tsc 基本使用

tsc(TypeScript Compiler)是官方提供的編譯工具。最簡單的編譯指令如下:

# 將單一檔案編譯成 JavaScript
tsc hello.ts

執行後會在同目錄產生 hello.js,內容與原始 TypeScript 基本相同,只是去除了型別宣告。

3. tsconfig.json:編譯設定的核心

在大型專案中,我們不會每次手動指定檔案與選項,而是透過 tsconfig.json 統一管理。以下是一個常見的範例:

{
  "compilerOptions": {
    "target": "ES2017",                // 產出語法目標
    "module": "ESNext",                // 使用的模組系統
    "strict": true,                    // 開啟所有嚴格型別檢查
    "esModuleInterop": true,           // 允許 CommonJS 模組的預設匯入
    "sourceMap": true,                 // 產生 .map 供除錯
    "outDir": "./dist",                // 輸出目錄
    "rootDir": "./src",                // 原始碼根目錄
    "removeComments": false,           // 保留註解(方便除錯)
    "noEmitOnError": true              // 有型別錯誤時不產出檔案
  },
  "include": ["src/**/*.ts"],          // 包含的檔案
  "exclude": ["node_modules"]          // 排除的檔案
}

重點說明

  • target 決定最終產出的 JavaScript 版本,常見值:ES5ES2015(ES6)、ES2020ESNext
  • module 控制匯入/匯出語法,CommonJS 用於 Node.js,ESNext 用於原生 ESM。
  • strict 開啟一系列嚴格檢查(noImplicitAnystrictNullChecks 等),是 最佳實踐

4. 常見的轉譯選項與範例

(1)降級至 ES5(支援 IE11)

{
  "compilerOptions": {
    "target": "ES5",
    "module": "CommonJS"
  }
}
// src/example.ts
const greet = (name: string): string => {
  return `Hello, ${name}!`;
};

export default greet;

編譯後的 dist/example.js 會變成:

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
function greet(name) {
    return "Hello, " + name + "!";
}
exports.default = greet;

(2)保留 ES6+ 語法(配合 Babel 或現代瀏覽器)

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext"
  }
}
// src/asyncDemo.ts
export async function fetchData(url: string): Promise<any> {
  const response = await fetch(url);
  return response.json();
}

編譯結果保留 async/await,讓後續的 Babel 或瀏覽器直接執行:

export async function fetchData(url) {
    const response = await fetch(url);
    return response.json();
}

(3)產生 Source Map 方便除錯

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "./dist"
  }
}

編譯時會同時產生 example.js.map,在 Chrome DevTools 中開啟「Enable source maps」即可看到原始的 TypeScript 程式碼。

(4)使用 incrementalwatch 提升開發效率

# 增量編譯,僅重新編譯變更的檔案
tsc --incremental

# 監聽檔案變化,自動編譯
tsc -w

5. 與其他工具的整合

工具 角色 為何要配合使用
Webpack 打包、模組合併 讓多個 .js 檔案一次性輸出為單一 bundle,減少 HTTP 請求
Babel 語法轉譯、Polyfill 即使 TypeScript 已降級至 ES5,仍可透過 Babel 加入最新語法的 polyfill(例如 Array.flat
ESLint + @typescript-eslint 程式碼風格、靜態檢查 在編譯前就捕捉潛在問題,避免不必要的編譯失敗

常見陷阱與最佳實踐

陷阱 說明 解決方式
忘記設定 noEmitOnError 編譯時仍產出 .js,導致錯誤的程式碼被部署 tsconfig.json 中加入 "noEmitOnError": true
target 與執行環境不匹配 目標設定過高(例如 ES2022)但執行環境僅支援 ES5,會跑出語法錯誤 依照部署環境(瀏覽器或 Node 版本)調整 target
使用 any 逃避型別檢查 失去 TypeScript 的最大優勢,程式碼變得不安全 開啟 noImplicitAny,盡量使用具體型別或 unknown
未產生 Source Map 除錯時只能看到編譯後的 JS,難以定位錯誤 設定 "sourceMap": true,或在 Webpack 中使用 devtool: 'source-map'
模組解析錯誤(CommonJS vs ESNext) 匯入/匯出語法在不同模組系統下行為不同,可能出現 default is not a function 的錯誤 根據執行環境統一 module 設定,並使用 esModuleInterop: true 來兼容 CommonJS 模組

最佳實踐

  1. 始終使用 strict:即使在小型專案,也建議開啟嚴格模式,能在開發早期捕捉到潛在問題。
  2. 把編譯交給腳本:在 package.json 中加入 buildwatch 指令,讓團隊成員只要執行 npm run build 即可得到正確產物。
    {
      "scripts": {
        "build": "tsc",
        "watch": "tsc -w"
      }
    }
    
  3. 分離開發與生產設定:使用 tsconfig.dev.json(開啟 sourceMapnoEmitOnError)和 tsconfig.prod.json(關閉 sourceMap、啟用 removeComments)來優化建置速度與檔案大小。
  4. 結合 CI/CD:在 CI pipeline 中加入 tsc --noEmit 只做型別檢查,確保每次 PR 都通過型別安全測試。

實際應用場景

1. 前端單頁應用(SPA)

在 React、Vue、Angular 等框架中,通常會使用 Webpack + ts-loaderVite + @vitejs/plugin-react-swc 來即時編譯 TypeScript。設定 target: "ES2017"module: "ESNext",讓瀏覽器直接執行 ES 模組,同時利用 Babel 處理尚未支援的語法。

2. Node.js 後端服務

Node.js 12+ 已原生支援 ES Modules,只要把 tsconfig.json 設為:

{
  "compilerOptions": {
    "module": "CommonJS",
    "target": "ES2020",
    "outDir": "./dist",
    "sourceMap": true
  }
}

再配合 ts-node-devts-node-dev --respawn src/index.ts)即可在開發階段直接執行未編譯的 TypeScript,省去每次手動 tsc 的流程。

3. 建立共用函式庫(npm package)

發布 npm 套件時,通常會同時輸出 兩種目標

  • dist/index.cjs.js(CommonJS)供 Node 使用
  • dist/index.esm.js(ESM)供現代前端工具鏈使用

透過 tsc多輸出 設定或結合 Rollup,一次完成兩種格式的編譯與打包,確保套件在不同環境都有最佳相容性。


總結

TypeScript 的編譯與轉譯 不只是把 .ts 變成 .js 那麼簡單。透過 tsctsconfig.json、Source Map、嚴格模式 等工具,我們可以在開發階段即時捕捉型別錯誤、產出符合目標環境的 JavaScript,並與 Webpack、Babel、ESLint 等生態系統無縫整合。

掌握以下要點,您就能在 實務專案 中自信地使用 TypeScript:

  1. 依需求設定 targetmodule,保證相容性與效能。
  2. 開啟 strictnoEmitOnErrorsourceMap,讓型別檢查與除錯更可靠。
  3. 使用增量編譯與 watch mode,提升開發迭代速度。
  4. 將編譯流程寫入 npm script,在團隊中保持一致的建置方式。

透過正確的編譯設定與最佳實踐,您不只能寫出 型別安全易維護 的程式碼,還能在部署時獲得 最小化、相容性佳 的 JavaScript,為專案的長期成功奠定堅實基礎。祝您在 TypeScript 的旅程中玩得開心、寫得順手!