泛型(Generics)是一種設計模式,允許開發者在撰寫程式時不必預先定義具體的型別,而是讓程式更具通用性與靈活性。這樣的設計可在函式、類別、介面中使用,實現不同型別的資料處理需求,並確保型別安全。
泛型的核心價值在於提供「型別靈活性」與「型別安全」。當開發者需要撰寫能處理多種型別的邏輯時,泛型可以減少重複程式碼,避免為每個型別撰寫不同版本的函式或類別,同時還能維持程式的強型別特性,避免潛在的型別錯誤。
假設我們有一個需求:將不同型別的資料轉換為陣列。如果不使用泛型,可能需要針對不同型別撰寫多個函式。
function toArrayFromNumber(num: number): number[] {
return [num];
}
function toArrayFromString(str: string): string[] {
return [str];
}
這種方法顯然不具備可擴展性,每增加一個型別,就需要新增一個函式版本。
泛型最常見的使用場景是函式,可以透過 toArrayByParam<T>
裡面的 T
定義泛型參數,且可以取任意名稱,不一定要是 T
,且可以定義多個泛型。
function toArrayByParam<T>(param: T): T[] {
return [param];
}
在使用函式時,可以明確的傳入型別,就會根據泛型來限制你傳的參數
const str1 = toArrayByParam<string>('明確的表示要 string 的泛型');
// str1 是 string[] 型別
// Error: 類型 'number' 的引數不可指派給類型 'string' 的參數。
const str2 = toArrayByParam<string>(0);
也可以利用類型推斷,不明確傳入型別,TypeScript 也會自動根據你傳的參數推斷出型別
const str3 = toArrayByParam('利用類型推斷是 string 的泛型');
// str3 推斷為 string[] 型別
const num = toArrayByParam(0);
// num 推斷為 number[] 型別
泛型不僅可以應用在函式上,還可以應用在類別中。這讓類別可以在定義時保持靈活性,並在實例化時確定具體的型別。
class Box<T> {
constructor(protected contents: T) {}
getContents(): T {
return this.contents;
}
}
const stringBox = new Box('Hello');
const content = stringBox.getContents();
// content 是 string 型別
泛型也可以與介面結合,來定義具有靈活型別的物件結構。
interface ApiResponse<T> {
data: T;
status: number;
message: string;
}
const userResponse: ApiResponse<{ name: string; age: number }> = {
data: { name: 'Johnson Mao', age: 18 },
status: 200,
message: 'Success'
};
在前面的章節中,我們已經多次接觸到泛型的應用,例如:
Record<string, boolean>
,用來表示一個鍵為 string
,值為 boolean
的物件。Array<number>
,用來表示一個數字型別的陣列。泛型在 TypeScript 中隨處可見,不僅適用於自定義資料結構,還廣泛應用於許多內建的 TypeScript 與 JavaScript 方法中。例如:
document.querySelector<HTMLInputElement>('#input-id');
Object.fromEntries
方法也可以透過泛型明確指定資料結構:Object.fromEntries<number>([]);
透過泛型,TypeScript 提供了極大的型別靈活性與安全性,讓開發者在實現強類型的同時,保持程式碼的靈活與可重用。