本文 AI 產出,尚未審核

TypeScript 工具型別 – Parameters<T>

簡介

在大型 TypeScript 專案中,我們常常需要根據既有函式的型別,動態產生其他型別或介面。手動維護這些衍生型別不僅繁瑣,還容易因為忘記同步更新而產生類型錯誤
Parameters<T> 正是為了這類需求而設計的 工具型別(Utility Type),它可以自動抽取函式 T 的參數型別,並以 元組(tuple)的形式回傳。藉由結合其他工具型別,我們能夠寫出更具彈性、可重用的程式碼,同時保持完整的型別安全。

本篇文章將從概念說明、實作範例、常見陷阱到最佳實踐,帶領讀者一步步掌握 Parameters<T> 的使用方式,並提供在真實專案中可直接套用的情境。


核心概念

1. Parameters<T> 的基本語法

Parameters<T> 的宣告非常簡潔:

type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
  • T 必須是函式型別(或可呼叫的物件)。
  • 透過條件型別 (T extends ... ? ... : ...) 搭配 infer,把 T 的參數型別推斷成 P,最後回傳 P
  • T 不是函式,結果會是 never,表示「不存在」的型別。

關鍵Parameters<T> 只會回傳參數的型別,不會包含回傳值或 this 參考。

2. 取得函式參數的元組型別

以下是一個最簡單的例子:

function greet(name: string, age: number): void {
  console.log(`Hello ${name}, ${age} years old`);
}

// 使用 Parameters 取得參數型別
type GreetParams = Parameters<typeof greet>; // [string, number]

GreetParams 會是一個 [string, number] 的元組,代表 greet 需要兩個參數,第一個是 string,第二個是 number

3. 與其他工具型別結合

Parameters<T> 常與 ReturnType<T>Partial<T>Pick<T, K> 等工具型別一起使用,形成複合型別

type GreetArgs = Parameters<typeof greet>;
type GreetPartial = Partial<GreetArgs>; // {[K in keyof GreetArgs]?: GreetArgs[K]}

GreetPartial 變成 [string?, number?],可用於 可選參數 的情境。

4. 針對泛型函式的使用

對於帶有泛型參數的函式,Parameters<T> 仍能正確推斷:

function identity<T>(value: T): T {
  return value;
}

// 取得 identity 的參數型別
type IdentityParams = Parameters<typeof identity>; // [unknown]

此時參數型別被推斷為 unknown,因為在沒有具體呼叫時,泛型 T 尚未被決定。若在具體呼叫後再取型別,則會得到具體型別:

const numIdentity = identity<number>;
type NumIdentityParams = Parameters<typeof numIdentity>; // [number]

5. 取出方法的參數型別

Parameters<T> 不只適用於普通函式,亦可用於類別或介面的 方法

class Api {
  fetch(url: string, timeout: number = 5000): Promise<string> {
    // ...
    return Promise.resolve('');
  }
}

// 取得類別方法的參數型別
type FetchParams = Parameters<Api['fetch']>; // [string, number?]

注意 timeout 有預設值,因而在型別上會變成 可選 (number?)。


程式碼範例

以下提供 5 個實務導向的範例,說明 Parameters<T> 如何在不同情境中發揮威力。

範例 1 – 自動產生 wrapper 函式

function logWrapper<F extends (...args: any[]) => any>(fn: F) {
  // 取得原函式的參數型別
  type Args = Parameters<F>;

  // 回傳一個與原函式簽名相同的新函式
  return (...args: Args): ReturnType<F> => {
    console.log('呼叫參數:', args);
    const result = fn(...args);
    console.log('回傳結果:', result);
    return result;
  };
}

// 原始函式
function add(a: number, b: number): number {
  return a + b;
}

// 包裝後的函式仍保有正確型別
const loggedAdd = logWrapper(add);
const sum = loggedAdd(3, 4); // sum 為 number

說明logWrapper 透過 Parameters<F> 取得傳入函式 fn 的參數型別,確保回傳的包裝函式在編譯期仍能正確檢查參數與回傳值。


範例 2 – 部分應用(Partial Application)

function multiply(a: number, b: number, c: number): number {
  return a * b * c;
}

// 產生只需要前兩個參數的函式
function partial<F extends (...args: any[]) => any>(
  fn: F,
  ...fixedArgs: Parameters<F>
) {
  return (...rest: any[]) => fn(...fixedArgs, ...rest);
}

// 固定前兩個參數
const double = partial(multiply, 2, 3);
const result = double(4); // 2 * 3 * 4 = 24

說明partial 使用 Parameters<F>fn 的全部參數型別捕捉下來,讓開發者可以安全地「固定」部分參數,剩餘的參數型別會自動推斷。


範例 3 – 產生 事件處理器 的型別

interface EventMap {
  click: (x: number, y: number) => void;
  keypress: (key: string) => void;
}

// 依據事件名稱自動取得對應的參數型別
type EventParams<E extends keyof EventMap> = Parameters<EventMap[E]>;

function on<E extends keyof EventMap>(event: E, handler: (...args: EventParams<E>) => void) {
  // 假設這裡會把 handler 註冊到某個系統
}

// 正確使用
on('click', (x, y) => console.log(`Clicked at (${x}, ${y})`));
on('keypress', key => console.log(`Key pressed: ${key}`));

說明:透過 Parameters 配合映射型別(Mapped Types),我們可以在 事件系統 中保證每個事件的回呼函式參數正確對應。


範例 4 – 自動生成 API 客戶端 的請求參數型別

type ApiMethod = (path: string, query?: Record<string, any>) => Promise<any>;

function createRequestFn<M extends ApiMethod>(method: M) {
  type Args = Parameters<M>; // [string, Record<string, any>?]

  return (...args: Args) => method(...args);
}

// 假設有一個 GET 方法
const get: ApiMethod = async (path, query) => {
  // ...實作
  return {};
};

const request = createRequestFn(get);
request('/users', { page: 2 });

說明:在自動生成的 API 客戶端中,Parameters 讓我們不必手動寫每個請求函式的參數型別,減少重複程式碼。


範例 5 – 測試工具:自動產生 mock 參數

function mockArgs<F extends (...args: any[]) => any>(fn: F): Parameters<F> {
  // 這裡簡單返回 undefined,實務上可根據型別產生隨機值
  return [] as unknown as Parameters<F>;
}

// 測試函式
function sendEmail(to: string, subject: string, body: string) {
  // 實際發信...
}

// 產生 mock 參數
const args = mockArgs(sendEmail); // 型別為 [string, string, string]
sendEmail(...args); // 編譯期保證參數數量與型別正確

說明:在單元測試或自動化測試中,我們常需要為函式產生「假」參數。mockArgs 只要傳入函式本身,就能得到正確的參數型別,避免手動錯寫。


常見陷阱與最佳實踐

陷阱 說明 建議的解決方式
傳入非函式型別 Parameters<T> 只能接受函式或可呼叫的物件,若傳入其他型別會得到 never,且在 IDE 中常看不到錯誤。 使用 條件型別extends (...args: any) => any 限制泛型,如 T extends (...args: any) => any
泛型函式推斷為 unknown 當函式本身是泛型且未具體化時,參數型別會被推斷為 unknown,導致後續使用受限。 在需要取得具體型別時,先 具體化 泛型(例如 const fn = genericFn<number>),再使用 Parameters<typeof fn>
預設參數與可選參數的差異 預設參數在型別層面會被視為 可選,但在實作時仍會被賦予預設值。若不注意,可能在呼叫時產生意外的 undefined 在使用 Parameters<T> 後,檢查結果是否包含 ?,必要時使用 Required<T> 重新強制必填。
多重 overload(重載) 若函式有多個 overload,Parameters<T> 只會取得最後一個 overload 的參數型別。 若需要取得所有 overload 的型別,可自行建立 聯合型別 或使用 OverloadParameters<T>(自行實作的工具型別)。
this 參數 this 參數不會被 Parameters<T> 包含,若函式依賴 this 需要額外處理。 使用 ThisParameterType<T> 取得 this 型別,或在方法上使用箭頭函式避免 this

最佳實踐

  1. 限制泛型範圍

    function foo<T extends (...args: any) => any>(fn: T) {
      type Args = Parameters<T>;
      // ...
    }
    

    這樣可以在編譯期立即捕捉錯誤。

  2. 結合 ReturnType<T>
    常見需求是同時取得參數與回傳值型別,寫成一個小工具會更方便:

    type FnSignature<T extends (...args: any) => any> = {
      args: Parameters<T>;
      return: ReturnType<T>;
    };
    
  3. 使用 as const 固定元組
    若要把 Parameters<T> 的結果作為 常量 使用(例如作為鍵值),加上 as const 可保留字面型別:

    const args = [] as const as Parameters<typeof someFn>;
    
  4. 在函式庫設計時公開型別
    若你在寫一個公共函式庫,建議把 Parameters<T> 包裝成 可讀性更高的型別,讓使用者不必直接面對泛型細節。


實際應用場景

1. 建構 高階函式(Higher‑Order Functions)

在 React、Vue 等框架中,常會寫 withLoggingwithErrorBoundary 之類的 HOC。Parameters<T> 可確保 HOC 接收的子函式參數型別保持一致,避免因改動子函式而忘記同步更新 HOC。

2. 動態生成 API 客戶端

大型後端服務往往有自動產生的 Swagger / OpenAPI 文件。使用 Parameters<T> 搭配程式碼產生器,可自動把每個 endpoint 的路徑、查詢參數型別抽取出來,形成 型別安全的 API 呼叫

3. 測試框架 的自動 mock

測試框架(如 Jest、Vitest)需要為被測試的函式產生假資料。Parameters<T> 可以直接告訴測試工具「這個函式需要什麼樣的參數」,進而自動產生符合型別的隨機值。

4. 事件總線(Event Bus)或 Redux 的 Action Creators

在 Redux、NgRx 等狀態管理工具中,Action Creator 的參數型別往往會隨著業務需求變動。使用 Parameters<T> 可以讓 dispatch 的型別自動跟隨 Action Creator 更新,減少手動維護的負擔。

5. CLI 工具 的指令解析

若你在寫一個 CLI 程式,指令的實作往往是多個函式,每個函式接受不同的參數。利用 Parameters<T> 可以把指令的參數型別直接映射到命令列解析器(如 yargs、commander),保證使用者輸入的參數在編譯期即被檢查。


總結

Parameters<T> 是 TypeScript 工具型別 中最實用的之一,讓開發者能夠自動抽取函式參數型別,進而在高階函式、API 客戶端、測試工具、事件系統等多種情境中保持型別的一致性與安全性。
使用時要注意 泛型推斷重載預設參數 等細節,並配合 ReturnType<T>ThisParameterType<T> 等其他工具型別,能打造出高度可組合易於維護的程式碼基礎。
只要遵循本文的最佳實踐與常見陷阱的警示,初學者也能快速上手,進而在中大型專案中發揮 TypeScript 靜態型別的最大威力。祝你寫程式寫得愉快、寫型別寫得安心!