本文 AI 產出,尚未審核

TypeScript

單元:函式(Functions)

主題:選擇性參數(optional parameter)


簡介

在日常開發中,函式往往需要接受不同數量的參數。若每一次都必須提供完整的參數列表,程式碼會變得冗長且不易維護。TypeScript 提供的 選擇性參數(optional parameter)機制,讓開發者可以在函式宣告時標示哪些參數是「可不傳」的,從而在保留型別安全的前提下,提升 API 的彈性與可讀性。

對於 初學者,了解選擇性參數能快速上手函式的多樣寫法;對 中級開發者,則能在大型專案中設計更具擴充性的介面、服務或工具函式,減少重複程式碼與錯誤風險。本文將從概念說明、實作範例、常見陷阱與最佳實踐,逐步帶你掌握在 TypeScript 中使用選擇性參數的技巧。


核心概念

1. 何謂選擇性參數?

在 TypeScript 中,函式參數若在名稱後加上 ?,即表示這個參數是「可選的」。呼叫函式時可以 省略 該參數,若省略,TypeScript 會自動將其視為 undefined

function greet(name: string, greeting?: string) {
  // 若 greeting 為 undefined,使用預設文字
  const msg = greeting ? `${greeting}, ${name}` : `Hello, ${name}`;
  console.log(msg);
}
  • name 為必填參數,必須傳入。
  • greeting 為選擇性參數,呼叫時可傳入或不傳入。

2. 位置規則

選擇性參數 必須放在參數列表的最後,或在其後的參數皆為選擇性或提供 預設值(default value)。這是因為在編譯階段,TypeScript 需要確定每個傳入值對應到哪個參數。

// 正確:選擇性參數在最後
function foo(a: number, b?: string) {}

/* 錯誤:選擇性參數在中間,會產生編譯錯誤
function bar(a?: number, b: string) {} // TS2551
*/

3. 與預設參數的關係

若同時使用 預設參數param = defaultValue)與 選擇性參數,兩者的行為相似,但語意上有差異:

  • 預設參數:即使呼叫者未提供值,參數仍會取得預設值,型別不會是 undefined(除非預設值本身為 undefined)。
  • 選擇性參數:若未提供,參數的型別會是 type | undefined,需要自行處理 undefined 的情況。
function log(message: string, level: string = "info") {
  console.log(`[${level}] ${message}`);
}
log("啟動完成"); // 輸出 [info] 啟動完成

程式碼範例

以下提供 5 個實用範例,說明不同情境下如何使用選擇性參數。

範例 1:簡易的問候函式

function greet(name: string, greeting?: string): void {
  const prefix = greeting ?? "Hello";
  console.log(`${prefix}, ${name}!`);
}

// 呼叫方式
greet("小明");                // Hello, 小明!
greet("小明", "嗨");          // 嗨, 小明!
  • 使用 ??(nullish coalescing)避免 greetingundefined 時的錯誤。

範例 2:計算矩形面積,允許省略高度(預設為寬度)

function rectangleArea(width: number, height?: number): number {
  // 若 height 為 undefined,使用 width 作為正方形的邊長
  const h = height ?? width;
  return width * h;
}

console.log(rectangleArea(5));      // 25  (5×5)
console.log(rectangleArea(5, 3));   // 15  (5×3)
  • 這個函式同時示範 選擇性參數預設值height ?? width)的結合。

範例 3:建立 API 請求參數物件

interface RequestOptions {
  method: "GET" | "POST" | "PUT" | "DELETE";
  timeout?: number;          // 毫秒,若未設定則使用全域預設
  headers?: Record<string, string>;
}

// 呼叫者只需要提供必要的 method,其他可自行省略
function request(url: string, options: RequestOptions) {
  const timeout = options.timeout ?? 5000; // 5 秒預設
  console.log(`Request ${options.method} ${url} (timeout=${timeout}ms)`);
}

// 範例呼叫
request("/users", { method: "GET" });
request("/posts", { method: "POST", timeout: 10000, headers: { "Content-Type": "application/json" } });
  • 透過介面 RequestOptions,把可選屬性標示為 ?,讓 API 使用者只傳入必要資訊。

範例 4:函式重載與選擇性參數

// 重載宣告
function format(value: number, locale?: string): string;
function format(value: Date, locale?: string): string;

// 實作
function format(value: number | Date, locale: string = "en-US"): string {
  if (typeof value === "number") {
    return value.toLocaleString(locale);
  } else {
    return value.toLocaleDateString(locale);
  }
}

// 使用
console.log(format(1234567));                 // 1,234,567 (en-US)
console.log(format(new Date(), "zh-TW"));    // 2025/11/19(依系統語系)
  • 透過 函式重載 搭配選擇性參數,使同一函式支援多種輸入型別,同時保留 locale 的彈性。

範例 5:使用 Partial<T> 產生可選屬性的物件

interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// 更新使用者資料時,只需要提供要變更的欄位
function updateUser(id: number, changes: Partial<User>) {
  // 假設此處會向後端發送 PATCH 請求
  console.log(`Update user ${id} with`, changes);
}

// 呼叫
updateUser(1, { email: "new@example.com" });
updateUser(2, { name: "Alice", age: 30 });
  • Partial<User> 會把 User 中的每個屬性都變成 選擇性,是 TypeScript 常見的實務技巧。

常見陷阱與最佳實踐

陷阱 說明 解決方式
選擇性參數放在必填參數之後 會導致編譯錯誤 A required parameter cannot follow an optional parameter. 必填參數一定要在前,或把後面的參數改為選擇性或提供預設值。
忘記處理 undefined 直接使用選擇性參數的值,可能在執行時拋出 TypeError 使用 ??、`
null 混用 nullundefined 在 TypeScript 中是不同型別,若函式接受 null,需額外聲明。 在型別中加上 `
過度使用選擇性參數 參數過多會讓函式簽名難以閱讀,且呼叫者不易知道哪些組合是合法的。 考慮改成 設定物件(options object)或使用 函式重載
預設值與選擇性參數同時使用 可能產生混淆:預設值會隱式地把參數變成必填,導致 ? 沒有意義。 若有預設值,通常不需要 ?,除非要保留 undefined 的語意。

最佳實踐

  1. 將多個可選參數封裝成 options 物件

    function connect(url: string, opts: { timeout?: number; secure?: boolean } = {}) { … }
    
    • 讓呼叫者只傳入需要的屬性,未提供的屬性自動 undefined
  2. 使用 nullish coalescing (??) 或 default 參數

    function foo(flag?: boolean) {
      const isEnabled = flag ?? true; // 預設為 true
    }
    
  3. 在函式內部盡早檢查 undefined,避免深層巢狀 if

  4. 開啟 strictNullChecks,讓編譯器強制你處理 undefinednull,提升程式安全性。

  5. 寫測試:針對有選擇性參數的函式,建立「全部傳入」與「省略」兩種測試案例,確保行為如預期。


實際應用場景

1. API 客戶端 SDK

在設計一個呼叫後端服務的 SDK 時,許多請求都有 共通的選項(如 timeoutheadersretry 次數)。使用選擇性參數或 options 物件,可讓開發者只關注需要的設定,而不必每次都傳入完整參數列表。

class HttpClient {
  get<T>(url: string, opts?: { timeout?: number; headers?: Record<string, string> }) {
    // 內部自動合併全域預設
  }
}

2. UI 元件庫

React / Vue 等框架的元件常常接收 可選的 props,例如按鈕的 iconsizedisabled。在 TypeScript 中,以 ? 標示可選屬性,讓使用者在 JSX 中僅寫必要的屬性,提升開發效率。

interface ButtonProps {
  label: string;
  onClick: () => void;
  disabled?: boolean;
  icon?: React.ReactNode;
}

3. CLI 工具或腳本

命令列工具的參數往往有 可選的旗標(flags)。在 Node.js 中使用 commanderyargs 等套件時,對應的 TypeScript 定義會利用選擇性參數表示這些旗標。

interface CliOptions {
  verbose?: boolean;
  configPath?: string;
}

4. 資料轉換或格式化函式

如日期、金額、數字的格式化函式,常需要接受「語系」或「樣式」等可選參數,讓使用者自行決定是否使用預設。

function formatCurrency(amount: number, locale?: string, currency?: string) {
  return new Intl.NumberFormat(locale ?? "en-US", {
    style: "currency",
    currency: currency ?? "USD",
  }).format(amount);
}

總結

  • 選擇性參數是 TypeScript 提供的語法糖,讓函式在保持型別安全的同時,具備彈性。
  • 必須遵守「選擇性參數必須放在最後」的規則,並留意 undefined 的處理方式。
  • 結合 預設參數nullish coalescingoptions 物件,可以寫出更易讀、易維護的 API。
  • 常見的陷阱包括參數位置錯誤、忘記檢查 undefined、以及過度使用選擇性參數導致簽名過於複雜。透過 最佳實踐(如使用 options 物件、開啟 strictNullChecks)可有效避免這些問題。
  • 在實務上,選擇性參數廣泛應用於 API 客戶端、UI 元件、CLI 工具、資料格式化 等場景,幫助開發者寫出更具彈性與可擴充性的程式碼。

掌握了選擇性參數的使用後,你將能在 TypeScript 專案中 更靈活地設計函式介面,同時保持程式的可讀性與安全性。祝你寫程式愉快,持續在 TypeScript 的世界裡探索更高階的型別技巧!