TypeScript 中的 import / export 超入門(從零開始)

| TypeScript | 4 Reads

核心先說:export 是為了讓「其他檔案」可以使用該成員;但在「同一檔案內」照樣能用,完全沒問題。
export 的東西則只能在該檔案內部使用,這是封裝(封裝/カプセル化)的基礎。


1. 為什麼需要模組?

當程式越寫越大時:

  • 全部寫在一個檔案 → 難維護、易衝突。

  • 模組化(Modules)→ 把功能分檔案,清楚分工。

  • 要讓 A 檔案用 B 檔案裡的函數 → B 必須 export,A 用 import 引入。

從此你可以建立自己的 工具庫、資料模型、API 包裝層


2. 什麼是 export

把某個變數、函數、類、型別等「公開」出去,讓別的檔案可以 import

2.1 命名匯出(Named Export)——最常用

// math.ts
export const PI = 3.14;
export function add(a: number, b: number) {
  return a + b;
}
export class Vector { /* ... */ }

2.2 集中匯出(列表寫法)

// math.ts
function sub(a: number, b: number) { return a - b; }
function mul(a: number, b: number) { return a * b; }

export { sub, mul }; // 這裡一次匯出多個已宣告的成員

2.3 匯出時改名

function mul(a:number,b:number){return a*b;}
export { mul as multiply };

2.4 預設匯出(Default Export)——每檔案最多一個

// greeter.ts
export default function greet(name: string) {
  return `Hello, ${name}!`;
}

命名匯出可以有很多個;default 匯出只能有一個。


3. 什麼是 import

在另一個檔案中引用別人 export 出的東西。

3.1 匯入命名匯出

import { PI, add, Vector } from './math';

3.2 匯入並改名

import { add as addNumbers } from './math';

3.3 匯入全部(命名空間物件)

import * as MathUtils from './math';
console.log(MathUtils.PI);
console.log(MathUtils.add(1, 2));

3.4 匯入預設匯出

import greet from './greeter';
console.log(greet('friedegg'));

3.5 同時匯入 default + named

import greet, { add, PI } from './utils';

4. 回答你的疑問:export 之後自己還能用嗎?

能!當然能!
export 只是「對外公開」;在同一檔案中,宣告後的成員你照常呼叫,不需要 import

// math.ts
export function add(a:number,b:number){return a+b;}

function selfTest() {
  // 同檔案內可以直接用 add
  console.log(add(2, 3));
}
selfTest();

5. 什麼時候不要 export

  • 只在該檔案內使用的輔助函數(helper/private utilities)

  • 暫時性測試資料(mock data)不想公開

  • 防止外部誤用內部細節(封裝設計)

const secret = 42;        // 不 export:外部拿不到
export function show() {  // 外部可呼叫
  console.log(secret);
}

6. 最小專案實作範例

6.1 檔案結構

my-ts-demo/
 ├─ src/
 │   ├─ math.ts
 │   └─ main.ts
 ├─ tsconfig.json
 └─ package.json

6.2 math.ts

export function add(a:number,b:number){return a+b;}
export function mul(a:number,b:number){return a*b;}

// 不對外公開
function debugLog(msg:string){ console.log('[math]', msg); }
debugLog('math loaded'); // 自己檔案內可以用

6.3 main.ts

import { add, mul } from './math';

console.log('2+3=', add(2,3));
console.log('4*5=', mul(4,5));

6.4 極簡 tsconfig.json

{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Node",
    "outDir": "dist",
    "strict": true,
    "esModuleInterop": true
  },
  "include": ["src"]
}

6.5 編譯與執行

npx tsc           # 產生 dist/
node dist/main.js # 執行

7. 類型專用匯入(進階但實用)

TypeScript 有「型別只用、不產生執行碼」的匯入方式,可減少編譯後 JS 的多餘依賴。

// user.ts
export interface User { id:number; name:string; }
export function hello(u:User){ console.log('Hello', u.name); }
// main.ts
import type { User } from './user'; // 只需要型別
import { hello } from './user';     // 執行時需要函數

const me: User = { id:1, name:'friedegg' };
hello(me);

8. Re-export(轉出口 / Barrel 模式)

建立「統一出口」,讓其他檔案匯入方便。

// index.ts (barrel)
export * from './math';
export { default as greet } from './greeter';

然後別的地方:

import { add, greet } from './index';

9. 常見錯誤與排雷

問題 原因 解法
Cannot find module './math' 路徑錯 檢查相對路徑、tsconfig baseUrl
匯入 default 結果 undefined 對方用 named export 改成 { foo } 匯入
is not a function 匯入名稱錯 匯入時名稱必須匹配 export 名
循環匯入(A 進 B,B 又進 A) 模組相互引用 抽公共層或延遲使用

10. FAQ

Q1:export 的東西是不是只給別的檔案用?
A:不是「只給」——它 可以 給別的檔案用;本檔案仍可正常使用。

Q2:那如果不 export,別人就拿不到?
A:對,這就是封裝;外部無法 import 未匯出的成員。

Q3:需要在自己檔案內 import 自己匯出的函數嗎?
A:不需要。同檔案內直接用。

Q4:預設匯出與命名匯出怎麼選?
A:小型工具庫習慣用命名匯出;如果此檔案概念明確、對外只提供一個主物件(例如類別或工廠函數),可用 default。


11. 快速對照小抄

// ----- 寫法 -----
export const X = 1;
export function foo(){}
export class Bar{}

function inner(){}
export { inner as Inner };

// ----- default -----
export default function main(){}

// ----- 匯入 -----
import { X, foo, Inner } from './mod';
import main from './mod';
import * as Mod from './mod';
import { foo as doFoo } from './mod';
import type { SomeType } from './mod';

12. 結語

掌握 export / import,你就能把程式碼拆分成乾淨、可維護的模組。
記住一句話就好:

「想讓別人用,就 export;不想給別人用,就別 export;自己都能用。」

返回目錄

This article was last edited at