TypeScript 泛型编程实战
TypeScript 的泛型系统是其最强大的特性之一。本文将全面介绍泛型编程的核心概念、高级技巧和实战应用。
一、泛型基础
1.1 什么是泛型?
泛型允许我们在定义函数、接口或类时使用类型参数,使这些类型可以在使用时确定。
1.2 基础泛型函数
function identity(arg: any): any { return arg; }
const output1 = identity<string>("hello"); const output2 = identity<number>(123);
function identity<T>(arg: T): T { return arg; }
const output3 = identity("hello"); const output4 = identity(123);
|
1.3 多个类型参数
function swap<T, U>(first: T, second: U): [U, T] { return [second, first]; }
const result = swap("hello", 123);
|
二、泛型约束
2.1 基础约束
function getLength<T extends { length: number }>(arg: T): number { return arg.length; }
console.log(getLength("hello")); console.log(getLength([1, 2, 3]));
|
2.2 多个约束
interface Lengthwise { length: number; }
function getLength<T extends Lengthwise>(arg: T): number { return arg.length; }
getLength("hello"); getLength([1, 2, 3]);
|
2.3 约束和类型参数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }
const user = { name: "张三", age: 25, email: "zhang@example.com" };
console.log(getProperty(user, "name")); console.log(getProperty(user, "age"));
|
三、泛型接口和类
3.1 泛型接口
interface Container<T> { value: T; }
const numberContainer: Container<number> = { value: 42 }; const stringContainer: Container<string> = { value: "hello" };
|
3.2 泛型类
class Box<T> { private value: T;
constructor(value: T) { this.value = value; }
getValue(): T { return this.value; }
setValue(value: T): void { this.value = value; } }
const numberBox = new Box<number>(100); const stringBox = new Box<string>("hello");
console.log(numberBox.getValue()); console.log(stringBox.getValue());
|
3.3 泛型方法和构造函数
class ApiClient<T> { private data: T[] = [];
add(item: T): void { this.data.push(item); }
getAll(): T[] { return this.data; } }
const apiClient = new ApiClient<{ id: number, name: string }>(); apiClient.add({ id: 1, name: "张三" }); apiClient.add({ id: 2, name: "李四" });
console.log(apiClient.getAll());
|
四、高级泛型
4.1 映射类型
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
type Partial<T> = { [P in keyof T]?: T[P]; };
interface User { name: string; age: number; email: string; }
type ReadonlyUser = Readonly<User>; type PartialUser = Partial<User>;
|
4.2 条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; type B = IsString<number>;
function isString(value: unknown): value is string { return typeof value === 'string'; }
if (isString(value)) { console.log(value.toUpperCase()); }
|
4.3 条件映射
type Exclude<T, U> = T extends U ? never : T;
type T = "a" | "b" | "c" | "d"; type B = Exclude<T, "b" | "d">;
|
4.4 映射约束
type WithRequired<T, K extends keyof T> = T & { [P in K]-?: T[P]; };
type WithOptional<T, K extends keyof T> = T & { [P in K]?: T[P]; };
interface User { name: string; age: number; email: string; }
type RequiredUser = WithRequired<User, "age" | "email">;
|
4.5 模板字面量类型
type EventName = `on${Capitalize<string>}`;
type ClickEvent = EventName<'click'>; type ChangeEvent = EventName<'change'>;
|
4.6 模式匹配
type Extract<T, U> = T extends U ? T : never;
type T = "a" | "b" | "c" | "d"; type B = Extract<T, "a" | "c">;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
interface User { name: string; age: number; email: string; password: string; }
type WithoutPassword = Omit<User, "password">;
|
五、泛型实战
5.1 管道函数
function pipe<T>(fn1: (arg: T) => T, fn2: (arg: T) => T): (arg: T) => T { return (arg: T): T => { return fn2(fn1(arg)); }; }
const toUpper = (str: string): string => str.toUpperCase(); const addExclamation = (str: string): string => `${str}!`;
const transform = pipe(toUpper, addExclamation); console.log(transform("hello"));
|
5.2 Promise 包装器
function promiseWrapper<T>(promise: Promise<T>): Promise<T> { return promise .then(result => ({ data: result, error: null } as const)) .catch(error => ({ data: null, error } as const)); }
type Result<T> = { data: T | null; error: Error | null; };
async function getData(): Promise<Result<User>> { const result = await promiseWrapper(fetch('/api/data')); if (result.error) { throw result.error; } return result.data; }
|
5.3 对比函数
function compare<T>(a: T, b: T): number { return a > b ? 1 : a < b ? -1 : 0; }
const numbers = [1, 2, 3, 4, 5]; numbers.sort(compare);
|
5.4 路由参数类型
type RouteParams = { '/users': { id: string }; '/posts': { id: string; userId: string }; '/search': { q: string; page?: number }; };
type GetRouteParam<Path extends keyof RouteParams, Param extends keyof RouteParams[Path]> = RouteParams[Path][Param];
type UserParams = GetRouteParam<'/users', 'id'>; type PostParams = GetRouteParam<'/posts', 'id'>;
|
5.5 状态管理
interface State<T> { data: T; loading: boolean; error: Error | null; }
function createState<T>(initial: T): State<T> { return { data: initial, loading: false, error: null, }; }
const userState = createState({ name: "张三", age: 25 });
|
5.6 路由匹配
type Route = { path: string; method: 'GET' | 'POST' | 'PUT' | 'DELETE'; handler: (req: Request) => Response; };
type Routes = Route[];
function matchRoute<Routes extends Route[]>(url: string, routes: Routes): Route | null { const [path, ...rest] = url.split('/').filter(Boolean);
for (const route of routes) { const routePath = route.path.split('/').filter(Boolean);
if (routePath[0] === path) { return route; } }
return null; }
|
六、泛型约束的进阶技巧
6.1 模板字面量类型
type EventName = `on${Capitalize<string>}`;
type ClickEvent = EventName<'click'>; type ChangeEvent = EventName<'change'>;
|
6.2 条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; type B = IsString<number>;
|
6.3 映射类型
type Readonly<T> = { readonly [P in keyof T]: T[P]; };
type Partial<T> = { [P in keyof T]?: T[P]; };
|
6.4 工具类型
type Pick<T, K extends keyof T> = { [P in K]: T[P]; };
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
interface User { id: number; name: string; age: number; email: string; }
type UserOnly = Omit<User, "id" | "age">;
|
七、泛型的最佳实践
7.1 合理使用泛型
function identity<T>(arg: T): T { return arg; }
function genericFunction<T>(arg1: T, arg2: T): T { return arg1; }
|
7.2 提供默认值
function createContainer<T>(value?: T): Container<T> { return { value: value || null }; }
function createContainer<T>(value: T): Container<T> { return { value }; }
|
7.3 清晰的类型定义
function getUser<T extends { id: number }>(id: number): Promise<T | null> { }
function getUser<T>(id: number): Promise<T> { }
|
八、常见错误和解决方案
8.1 类型推断问题
function map<T>(arr: T[], fn: (item: T) => T): T[] { return arr.map(fn); }
function map<T, R>(arr: T[], fn: (item: T) => R): R[] { return arr.map(fn); }
|
8.2 约束不充分
function getProperty<T>(obj: T, key: string) { return obj[key]; }
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; }
|
九、总结
9.1 泛型的核心要点
- 类型参数:在定义时使用,在实例化时确定
- 类型推断:自动推断类型参数
- 泛型约束:限制类型参数的范围
- 工具类型:提供常见的类型转换
9.2 常用工具类型
Partial<T>:部分类型Readonly<T>:只读类型Pick<T, K>:选择类型Omit<T, K>:排除类型Record<K, T>:记录类型Exclude<T, U>:排除类型Extract<T, U>:提取类型
掌握泛型,写出更强大、更安全的 TypeScript 代码!