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)避免greeting為undefined時的錯誤。
範例 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 混用 |
null 與 undefined 在 TypeScript 中是不同型別,若函式接受 null,需額外聲明。 |
在型別中加上 ` |
| 過度使用選擇性參數 | 參數過多會讓函式簽名難以閱讀,且呼叫者不易知道哪些組合是合法的。 | 考慮改成 設定物件(options object)或使用 函式重載。 |
| 預設值與選擇性參數同時使用 | 可能產生混淆:預設值會隱式地把參數變成必填,導致 ? 沒有意義。 |
若有預設值,通常不需要 ?,除非要保留 undefined 的語意。 |
最佳實踐
將多個可選參數封裝成 options 物件
function connect(url: string, opts: { timeout?: number; secure?: boolean } = {}) { … }- 讓呼叫者只傳入需要的屬性,未提供的屬性自動
undefined。
- 讓呼叫者只傳入需要的屬性,未提供的屬性自動
使用
nullish coalescing(??) 或default參數function foo(flag?: boolean) { const isEnabled = flag ?? true; // 預設為 true }在函式內部盡早檢查
undefined,避免深層巢狀if。開啟
strictNullChecks,讓編譯器強制你處理undefined與null,提升程式安全性。寫測試:針對有選擇性參數的函式,建立「全部傳入」與「省略」兩種測試案例,確保行為如預期。
實際應用場景
1. API 客戶端 SDK
在設計一個呼叫後端服務的 SDK 時,許多請求都有 共通的選項(如 timeout、headers、retry 次數)。使用選擇性參數或 options 物件,可讓開發者只關注需要的設定,而不必每次都傳入完整參數列表。
class HttpClient {
get<T>(url: string, opts?: { timeout?: number; headers?: Record<string, string> }) {
// 內部自動合併全域預設
}
}
2. UI 元件庫
React / Vue 等框架的元件常常接收 可選的 props,例如按鈕的 icon、size、disabled。在 TypeScript 中,以 ? 標示可選屬性,讓使用者在 JSX 中僅寫必要的屬性,提升開發效率。
interface ButtonProps {
label: string;
onClick: () => void;
disabled?: boolean;
icon?: React.ReactNode;
}
3. CLI 工具或腳本
命令列工具的參數往往有 可選的旗標(flags)。在 Node.js 中使用 commander、yargs 等套件時,對應的 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 coalescing、options 物件,可以寫出更易讀、易維護的 API。
- 常見的陷阱包括參數位置錯誤、忘記檢查
undefined、以及過度使用選擇性參數導致簽名過於複雜。透過 最佳實踐(如使用 options 物件、開啟 strictNullChecks)可有效避免這些問題。 - 在實務上,選擇性參數廣泛應用於 API 客戶端、UI 元件、CLI 工具、資料格式化 等場景,幫助開發者寫出更具彈性與可擴充性的程式碼。
掌握了選擇性參數的使用後,你將能在 TypeScript 專案中 更靈活地設計函式介面,同時保持程式的可讀性與安全性。祝你寫程式愉快,持續在 TypeScript 的世界裡探索更高階的型別技巧!