TypeScript 泛型深度解析:從基礎(chǔ)到高級(jí)應(yīng)用
泛型是 TypeScript 最閃耀的特性之一,它如同代碼世界的"萬能模具",讓開發(fā)者能夠鑄造出靈活且類型安全的組件。本文將帶您深入探索泛型的核心機(jī)制,并通過真實(shí)場(chǎng)景案例揭示其強(qiáng)大威力。
一、泛型基礎(chǔ):類型世界的變形金剛
1.1 為何需要泛型?
在傳統(tǒng)類型系統(tǒng)中,我們經(jīng)常要為不同數(shù)據(jù)類型編寫重復(fù)代碼。比如處理數(shù)字和字符串的 identity 函數(shù):
// 重復(fù)勞動(dòng)示例
function identityNumber(arg: number): number {
return arg;
}
function identityString(arg: string): string {
return arg;
}
泛型的出現(xiàn)完美解決了這種冗余,它允許我們創(chuàng)建可適應(yīng)多種類型的代碼模板[^1]。
1.2 泛型函數(shù)實(shí)現(xiàn)
function identity<T>(arg: T): T {
return arg;
}
// 自動(dòng)類型推斷
const strOutput = identity('TS'); // string 類型
const numOutput = identity(2024); // number 類型
二、泛型應(yīng)用場(chǎng)景全解析
泛型
2.1 集合操作:類型安全的容器
// 泛型數(shù)組處理
function reverseArray<T>(items: T[]): T[] {
return [...items].reverse();
}
const numbers = reverseArray([1, 2, 3]); // number[]
const letters = reverseArray(['a', 'b']); // string[]
2.2 數(shù)據(jù)建模:通用接口設(shè)計(jì)
// 通用響應(yīng)接口
interface ApiResponse<T> {
status: number;
data: T;
timestamp: Date;
}
// 用戶數(shù)據(jù)響應(yīng)
const userResponse: ApiResponse<User> = {
status: 200,
data: { id: 1, name: 'Alice' },
timestamp: new Date(),
};
// 產(chǎn)品列表響應(yīng)
const productResponse: ApiResponse<Product[]> = {
status: 200,
data: [{ id: 1001, price: 99 }],
timestamp: new Date(),
};
2.3 類庫開發(fā):可復(fù)用的組件
// 通用存儲(chǔ)器
class GenericStorage<T> {
private items: T[] = [];
addItem(item: T): void {
this.items.push(item);
}
getLatest(): T | undefined {
return this.items.slice(-1)[0];
}
}
// 使用示例
const numberStorage = new GenericStorage<number>();
numberStorage.addItem(42);
console.log(numberStorage.getLatest()); // 42
const userStorage = new GenericStorage<User>();
userStorage.addItem({ id: 2, name: 'Bob' });
三、高級(jí)泛型技巧
3.1 類型約束:給泛型戴上手銬
// 確保類型具有l(wèi)ength屬性
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): void {
console.log(arg.length);
}
logLength('Hello'); // 5
logLength([1, 2, 3]); // 3
// logLength(2024); // 錯(cuò)誤:number沒有l(wèi)ength屬性
3.2 多重類型參數(shù)
// 元組生成器
function createPair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const boolStrPair = createPair(true, 'yes'); // [boolean, string]
const numDatePair = createPair(10, new Date()); // [number, Date]
3.3 默認(rèn)類型參數(shù)
// 帶默認(rèn)值的泛型
interface Pagination<T = string> {
current: number;
pageSize: number;
data: T[];
}
const stringPagination: Pagination = {
current: 1,
pageSize: 10,
data: ['item1', 'item2'],
};
const numberPagination: Pagination<number> = {
current: 2,
pageSize: 20,
data: [1, 2, 3],
};
四、實(shí)戰(zhàn)應(yīng)用案例
4.1 表單驗(yàn)證器
interface Validator<T> {
isValid(value: T): boolean;
}
// 數(shù)字范圍驗(yàn)證
class NumberRangeValidator implements Validator<number> {
constructor(private min: number, private max: number) {}
isValid(value: number): boolean {
return value >= this.min && value <= this.max;
}
}
// 郵箱格式驗(yàn)證
class EmailValidator implements Validator<string> {
private regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
isValid(value: string): boolean {
return this.regex.test(value);
}
}
4.2 API 請(qǐng)求封裝
async function fetchData<T>(url: string): Promise<T> {
const response = await fetch(url);
if (!response.ok) throw new Error('Request failed');
return response.json();
}
// 使用示例
interface UserData {
id: number;
name: string;
}
const loadUser = async (userId: number) => {
try {
const user = await fetchData<UserData>(`/api/users/${userId}`);
console.log(user.name);
} catch (error) {
console.error('加載用戶失敗:', error);
}
};
五、最佳實(shí)踐與陷阱規(guī)避
推薦做法:
- 優(yōu)先使用自動(dòng)類型推斷
- 為復(fù)雜泛型添加類型注釋
- 合理使用泛型約束保證安全
常見錯(cuò)誤:
- 過度使用 any 類型
- 忽略類型參數(shù)約束
- 創(chuàng)建過于復(fù)雜的泛型結(jié)構(gòu)
六、未來展望:泛型的進(jìn)階之路
隨著 TypeScript 的持續(xù)發(fā)展,泛型應(yīng)用正在向更高級(jí)的領(lǐng)域延伸:
- 條件類型:實(shí)現(xiàn)類型層面的條件判斷
- 映射類型:批量轉(zhuǎn)換類型結(jié)構(gòu)
- 類型體操:構(gòu)建復(fù)雜的類型系統(tǒng)
// 條件類型示例
type NonNullable<T> = T extends null | undefined ? never : T;
type ValidString = NonNullable<string | null>; // string