本文 AI 產出,尚未審核

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

主題:路徑別名(paths)


簡介

在大型前端或 Node.js 專案中,檔案結構往往相當複雜,相對路徑 (../../..) 很容易寫錯、閱讀困難、且在重構時需要大量手動調整。TypeScript 提供的 paths 設定讓我們可以為特定目錄或模組建立自訂別名,從而以簡潔、可讀的方式引用檔案。

使用路徑別名不僅提升開發效率,還能減少錯誤、加速 IDE 的自動補全與跳轉功能,更重要的是,它在編譯階段會自動轉換為正確的相對路徑,保證最終產出的 JavaScript 仍能在執行環境中正確載入。

本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,完整介紹 TypeScript 的 paths 功能,幫助初學者快速上手,也讓中階開發者在既有專案中安全導入。


核心概念

1. tsconfig.json 中的 baseUrlpaths

屬性 說明 必要性
baseUrl 設定別名解析的根目錄,通常是專案的 src 資料夾。若未設定,paths 會失效。 必須
paths 一個鍵值對映射,左側為別名(可以使用通配符 *),右側為實際的相對路徑(同樣支援 *)。 可選,但配合 baseUrl 使用時非常實用

重點paths 只在 編譯階段 產生作用,執行環境(Node、瀏覽器)仍需要相對路徑或額外的模組解析工具(如 webpack、ts-node、vite)配合。

2. 通配符 (*) 的運用

  • * 代表 任意字串,可用於建立「目錄級」或「檔案級」的別名。
  • 右側的 * 必須與左側的 * 對應位置,否則編譯會報錯。

3. 為什麼需要 paths 而不是直接使用 module-alias

  • paths 是 TypeScript 原生支援,型別檢查IDE 補全 完全相容。
  • module-alias 只在 Node 執行階段有效,若同時使用 Webpack/Vite,需要額外設定兩套別名,維護成本較高。

程式碼範例

以下示範 5 個常見情境,從最簡單的單檔別名到結合通配符的多層目錄別名。

範例 1:最基礎的單檔別名

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@utils": ["utils/index.ts"]
    }
  }
}

使用方式

// src/main.ts
import { formatDate } from "@utils";

console.log(formatDate(new Date()));

說明@utils 直接對應到 src/utils/index.ts,省去 ../../utils 的相對路徑。


範例 2:目錄別名(無通配符)

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@components/*": ["components/*"]
    }
  }
}

使用方式

// src/pages/Home.tsx
import Header from "@components/Header";
import Footer from "@components/Footer";

export default function Home() {
  return (
    <>
      <Header />
      <Footer />
    </>
  );
}

說明@components/* 代表 src/components 目錄下的所有檔案,對應關係為 @components/Headersrc/components/Header.tsx


範例 3:多層目錄別名(使用兩個 *

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": "./src",
    "paths": {
      "@services/*/*": ["services/*/*"]
    }
  }
}

使用方式

// src/app.ts
import { AuthService } from "@services/auth/local";
import { PaymentService } from "@services/payment/stripe";

new AuthService().login();
new PaymentService().charge();

說明:左側的 */* 會分別映射到右側的 */*,因此 @services/auth/localsrc/services/auth/local.ts


範例 4:結合第三方套件的別名(例如 src/@types

tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@app/*": ["src/*"],
      "@types/*": ["src/@types/*"]
    }
  }
}

使用方式

// src/models/User.ts
import { Role } from "@types/role";

export interface User {
  id: number;
  name: string;
  role: Role;
}

說明:將自訂型別放在 src/@types,透過 @types/* 直接引用,避免在每個檔案裡寫長長的相對路徑。


範例 5:在 Vite / Webpack 中同步設定別名

重點paths 只影響 TypeScript 編譯,若使用 Vite、Webpack、Rollup 等 bundler,仍需在它們的設定檔中同步別名,否則執行階段會找不到模組。

Vite (vite.config.ts)

import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";

export default defineConfig({
  plugins: [tsconfigPaths()], // 直接讀取 tsconfig.json 的 paths
});

Webpack (webpack.config.js)

const path = require("path");

module.exports = {
  // ...
  resolve: {
    alias: {
      "@components": path.resolve(__dirname, "src/components"),
      "@utils": path.resolve(__dirname, "src/utils")
    },
    extensions: [".ts", ".js", ".json"]
  }
};

說明:Vite 提供 vite-tsconfig-paths 插件可自動同步;Webpack 需要手動對應 alias


常見陷阱與最佳實踐

陷阱 可能的結果 解決方案
忘記設定 baseUrl paths 完全不生效,編譯會報 Cannot find module 錯誤。 compilerOptions 中必須先設定 "baseUrl": "./src"(或專案根目錄)。
通配符位置不對稱 例如 "@api/*": ["services/api"] → 右側缺少 *,導致所有別名都指向同一檔案。 確保左、右兩側的 * 數量與位置相同。
IDE 沒有自動補全 VS Code 仍顯示紅色波浪線。 安裝 TypeScript Vue PluginVetur,並確保 tsconfig.json 位於工作區根目錄。
執行環境找不到模組 Node 執行時拋出 MODULE_NOT_FOUND 在 Node (ts-node) 或 bundler 中同步別名,或使用 module-alias 作為補償。
別名衝突 兩個別名指向相同路徑或相互覆蓋。 定義別名時保持唯一性,並在 paths 中使用更具體的鍵名。

最佳實踐

  1. 統一 baseUrlsrc:大多數專案的程式碼都放在 src,設定為根目錄可減少 ../ 的層級。
  2. 使用「前綴」命名規則:如 @components@services@utils,讓別名一目了然。
  3. 保持 paths 與 bundler 設定同步:建議使用 vite-tsconfig-pathstsconfig-paths-webpack-plugin 等自動同步工具,減少手動錯誤。
  4. 在 monorepo 中使用相對路徑:若有多個子套件,paths 應指向 packages/*/src,並在根 tsconfig.json 中配置 references
  5. 在 CI/CD 中加入檢查:使用 tsc --noEmiteslint-plugin-import 確認別名解析無誤,防止部署時出現找不到模組的問題。

實際應用場景

1. 大型企業級前端應用

在一個包含上百個 UI 元件的 React 專案中,使用 @components/* 能讓開發者在任何子模組中直接 import Button from "@components/Button",減少路徑錯誤,並讓新加入的同事快速上手。

2. Node.js 後端微服務

微服務往往把共用的工具、型別、錯誤類別放在 libs 資料夾。透過 @libs/* 別名,服務內部的 import { HttpError } from "@libs/error" 變得清晰,同時在不同服務之間共享同一套 tsconfig.json,保持一致性。

3. Monorepo(如 Nx、Lerna)

在 monorepo 中,每個子套件都有自己的 src。根 tsconfig.base.json 可以這樣設定:

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@app/*": ["apps/*/src"],
      "@pkg/*": ["packages/*/src"]
    }
  }
}

如此一來,任意子套件都能使用 import { Foo } from "@pkg/common",不必關心實際的相對位置。

4. 跨平台(React Native + Web)

React Native 與 Web 端共用同一套業務邏輯時,使用 @shared/* 別名可以讓兩個平台共用相同的 import 語句,減少平台差異帶來的維護成本。


總結

  • paths 是 TypeScript 提供的路徑別名機制,配合 baseUrl 能讓專案的模組引用變得簡潔、可讀且易於重構。
  • 正確使用 通配符、保持 IDE 與 bundler 設定同步,是避免執行階段錯誤的關鍵。
  • 在大型專案、微服務或 monorepo 中,建立一致的別名規範能顯著提升開發效率與維護性。
  • 最佳實踐包括:統一 baseUrl、使用前綴命名、同步 bundler 設定、在 CI 中加入別名檢查。

透過本文的概念說明與實作範例,你已經可以在自己的 TypeScript 專案裡安全地導入 路徑別名,讓程式碼更乾淨、更具可維護性。快把這些技巧應用到實際專案中,體驗開發效率的提升吧!