首先先看一個簡單的例子
function identity<T>(arg: T): Type {
return arg;
}
const stringOutput = identity<string>("Hello World");
const numberOutput = identity<number>(25);
這是一個泛型函式,主要由兩個部分構成:
T
,可以使用任意名稱。但通常習慣使用 T
表示,代表 "Type" 。這個參數允許我們在撰寫程式時保持型別彈性。除了 T 以外,以下是一些常見的泛型參數:
U
:當你需要多個泛型參數時,會使用 U 來表示第二個型別參數。K
(Key):物件鍵值的泛型V
(Value):表示與 K 配對的值的型別。這個泛型參數通常與 K 搭配,定義物件中鍵對應的值的型別。string
和 number
,使得程式碼可以適應不同型別的需求。any
可以解決部份問題,但泛型讓我們可以編寫適用於不同類型數據的代碼,仍然保持 TypeScript 的類型安全性,避免了丟失型別檢查的風險。我們可以透過 extends
將泛型限制於某些特定型別。舉個例子,如果我們想限制 identity 函式只能接受 number
或 string
:
function identity<T extends number | string>(arg: T): T {
return arg;
}
const stringOutput = identity<string>("Hello World");
const numberOutput = identity<number>(25);
這樣我們就限制了 T
必須是 number
或 string
型別。
extends
除了限制型別外,也可以用來確保傳入的物件具備特定屬性,會在接下來的例子說明。
接下來,以一個列表(List)元件為例,展示如何在 React 中使用泛型來定義元件的 props 型別:
type ListProps<T> = {
items: T[];
renderItem: (item: T) => React.ReactNode;
};
function List<T extends { id: string | number }>({
items,
renderItem,
}: ListProps<T>) {
return (
<ul>
{items.map((item) => (
<li key={item.id}>{renderItem(item)}</li>
))}
</ul>
);
}
在這個例子中,List 元件接受一個 items
陣列,其元素型別由泛型 T
決定,並透過 renderItem
函數來渲染每個元素。使用泛型讓這個元件可以處理各種型別的資料,並確保每個資料項目都有 id
屬性。
除了元件外,泛型也常用於自訂 Hook 的型別定義。
以 useFetch 為例
import { useState, useEffect } from "react";
type FetchState<T> = {
data: T | null;
error: Error | null;
loading: boolean;
};
export function useFetch<T>(url: string): FetchState<T> {
const [state, setState] = useState<FetchState<T>>({
data: null,
error: null,
loading: true,
});
useEffect(() => {
const fetchData = async () => {
setState({ data: null, error: null, loading: true });
try {
const response = await fetch(url);
const data = await response.json();
setState({ data, error: null, loading: false });
} catch (error) {
setState({
data: null,
error: error instanceof Error ? error : new Error("Unknown error"),
loading: false,
});
}
};
fetchData();
}, [url]);
return state;
}
這邊使用了泛型,因為 fetch 的回傳值可能會是不同的型別。
補充: 以上程式碼都可以再更優化,這邊只是讓大家了解泛型帶來的好處。未來怎麼優化可以參考之後的文章。
希望透過以上的分享能讓大家更了解泛型。
至於許多常見的 Utility Types 等都是針對以上概念去做延伸。
參考資料:
https://www.typescriptlang.org/docs/handbook/2/generics.html