·43 分钟
TypeScript 泛型深入指南:从基础到高级
TypeScript泛型类型系统前端前端
泛型基础
什么是泛型
泛型(Generics)是一种参数化类型,允许你创建可重用的组件,这些组件可以支持多种类型而不是单一类型。
typescript
// 没有泛型:需要为每种类型写重复代码
function identityNumber(arg: number): number {
return arg;
}
function identityString(arg: string): string {
return arg;
}
// 使用泛型:一个函数支持多种类型
function identity<T>(arg: T): T {
return arg;
}
// 使用
const num = identity<number>(42);
const str = identity<string>("hello");
const bool = identity<boolean>(true);
类型推断
typescript
function identity<T>(arg: T): T {
return arg;
}
// TypeScript 可以推断类型
const num = identity(42); // 推断为 number
const str = identity("hello"); // 推断为 string
// 显式指定类型
const explicit = identity<string>("hello");
多个泛型参数
typescript
function pair<T, U>(first: T, second: U): [T, U] {
return [first, second];
}
const p = pair<string, number>("hello", 42);
const p2 = pair("hello", 42); // 类型推断
// 实际应用
function map<T, U>(array: T[], fn: (item: T) => U): U[] {
return array.map(fn);
}
const numbers = [1, 2, 3];
const strings = map(numbers, n => n.toString());
泛型约束
基本约束
typescript
// 约束泛型必须有 length 属性
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(arg: T): T {
console.log(arg.length);
return arg;
}
// 有效
logLength("hello"); // string 有 length
logLength([1, 2, 3]); // array 有 length
logLength({ length: 10 }); // 对象有 length
// 无效
logLength(42); // number 没有 length
logLength(true); // boolean 没有 length
keyof 约束
typescript
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const person = { name: "Alice", age: 30 };
const name = getProperty(person, "name"); // string
const age = getProperty(person, "age"); // number
// 错误:'email' 不是 person 的属性
// const email = getProperty(person, "email");
类型约束
typescript
// 约束为特定类型
function createArray<T extends string | number>(length: number, value: T): T[] {
return Array(length).fill(value);
}
const strings = createArray(3, "hello"); // string[]
const numbers = createArray(3, 42); // number[]
// 错误:boolean 不满足约束
// const bools = createArray(3, true);
泛型接口和类
泛型接口
typescript
// 泛型接口
interface Box<T> {
value: T;
getValue(): T;
setValue(value: T): void;
}
// 实现
class NumberBox implements Box<number> {
constructor(private _value: number) {}
getValue(): number {
return this._value;
}
setValue(value: number): void {
this._value = value;
}
}
// 泛型实现
class GenericBox<T> implements Box<T> {
constructor(private _value: T) {}
getValue(): T {
return this._value;
}
setValue(value: T): void {
this._value = value;
}
}
const box = new GenericBox<string>("hello");
box.getValue(); // "hello"
box.setValue("world");
泛型类
typescript
class Stack<T> {
private items: T[] = [];
push(item: T): void {
this.items.push(item);
}
pop(): T | undefined {
return this.items.pop();
}
peek(): T | undefined {
return this.items[this.items.length - 1];
}
isEmpty(): boolean {
return this.items.length === 0;
}
size(): number {
return this.items.length;
}
}
// 使用
const numberStack = new Stack<number>();
numberStack.push(1);
numberStack.push(2);
numberStack.pop(); // 2
const stringStack = new Stack<string>();
stringStack.push("hello");
stringStack.push("world");
泛型接口作为函数类型
typescript
interface SearchFunc<T> {
(source: T[], target: T): number;
}
const linearSearch: SearchFunc<number> = (source, target) => {
for (let i = 0; i < source.length; i++) {
if (source[i] === target) return i;
}
return -1;
};
const binarySearch: SearchFunc<number> = (source, target) => {
let left = 0;
let right = source.length - 1;
while (left <= right) {
const mid = Math.floor((left + right) / 2);
if (source[mid] === target) return mid;
if (source[mid] < target) left = mid + 1;
else right = mid - 1;
}
return -1;
};
泛型工具类型
Partial 和 Required
typescript
interface User {
id: number;
name: string;
email: string;
age: number;
}
// Partial:所有属性变为可选
type PartialUser = Partial<User>;
// 等价于:
// {
// id?: number;
// name?: string;
// email?: string;
// age?: number;
// }
// Required:所有属性变为必选
type RequiredUser = Required<PartialUser>;
// 等价于 User
// 实际应用
function updateUser(user: User, updates: Partial<User>): User {
return { ...user, ...updates };
}
const user: User = {
id: 1,
name: "Alice",
email: "alice@example.com",
age: 30
};
const updated = updateUser(user, { age: 31 });
Readonly
typescript
// Readonly:所有属性变为只读
type ReadonlyUser = Readonly<User>;
const user: ReadonlyUser = {
id: 1,
name: "Alice",
email: "alice@example.com",
age: 30
};
// 错误:只读属性不能修改
// user.name = "Bob";
// 实际应用:不可变数据
function createUser(data: Readonly<User>): User {
return { ...data };
}
Pick 和 Omit
typescript
// Pick:选取部分属性
type UserBasic = Pick<User, "id" | "name">;
// 等价于:
// {
// id: number;
// name: string;
// }
// Omit:排除部分属性
type UserWithoutEmail = Omit<User, "email">;
// 等价于:
// {
// id: number;
// name: string;
// age: number;
// }
// 实际应用
function getUserBasic(id: number): UserBasic {
return { id, name: "Alice" };
}
function createUserProfile(data: Omit<User, "id">): User {
return { id: Date.now(), ...data };
}
Record
typescript
// Record:构造键值对类型
type UserRoles = Record<string, string[]>;
const roles: UserRoles = {
admin: ["read", "write", "delete"],
user: ["read"],
guest: []
};
// 实际应用
type PageInfo = {
title: string;
url: string;
};
type Pages = Record<"home" | "about" | "contact", PageInfo>;
const pages: Pages = {
home: { title: "Home", url: "/" },
about: { title: "About", url: "/about" },
contact: { title: "Contact", url: "/contact" }
};
Exclude 和 Extract
typescript
type Status = "active" | "inactive" | "pending" | "deleted";
// Exclude:从联合类型中排除
type ActiveStatus = Exclude<Status, "deleted" | "inactive">;
// 等价于 "active" | "pending"
// Extract:从联合类型中提取
type NonActiveStatus = Extract<Status, "inactive" | "deleted">;
// 等价于 "inactive" | "deleted"
// 实际应用
function activateUser(status: Exclude<Status, "deleted">) {
// 处理激活逻辑
}
NonNullable
typescript
type MaybeString = string | null | undefined;
// NonNullable:排除 null 和 undefined
type DefinitelyString = NonNullable<MaybeString>;
// 等价于 string
// 实际应用
function processValue(value: NonNullable<MaybeString>) {
// value 保证是 string
console.log(value.toUpperCase());
}
高级泛型模式
条件类型
typescript
// 条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// 实际应用
type ArrayOrSingle<T> = T extends any[] ? T : T[];
function ensureArray<T>(value: T): ArrayOrSingle<T> {
return Array.isArray(value) ? value : [value] as any;
}
const arr1 = ensureArray([1, 2, 3]); // number[]
const arr2 = ensureArray(42); // number[]
映射类型
typescript
// 映射类型
type Optional<T> = {
[K in keyof T]?: T[K];
};
type Readonly2<T> = {
readonly [K in keyof T]: T[K];
};
// 实际应用:表单状态
type FormState<T> = {
[K in keyof T]: {
value: T[K];
error: string | null;
touched: boolean;
};
};
interface LoginForm {
username: string;
password: string;
}
type LoginFormState = FormState<LoginForm>;
// 等价于:
// {
// username: { value: string; error: string | null; touched: boolean; };
// password: { value: string; error: string | null; touched: boolean; };
// }
模板字面量类型
typescript
// 模板字面量类型
type EventName = "click" | "scroll" | "mousemove";
type EventHandlerName = `on${Capitalize<EventName>}`;
// 等价于 "onClick" | "onScroll" | "onMousemove"
// 实际应用
type CSSProperty = "margin" | "padding" | "border";
type CSSDirection = "top" | "right" | "bottom" | "left";
type CSSClassName = `${CSSProperty}-${CSSDirection}`;
// 等价于 "margin-top" | "margin-right" | ... | "border-left"
// 构建 API 路径
type ApiVersion = "v1" | "v2";
type ApiResource = "users" | "posts" | "comments";
type ApiPath = `/api/${ApiVersion}/${ApiResource}`;
// 等价于 "/api/v1/users" | "/api/v1/posts" | ... | "/api/v2/comments"
infer 关键字
typescript
// infer:在条件类型中推断
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type A = ReturnType<() => string>; // string
type B = ReturnType<(x: number) => boolean>; // boolean
// 推断函数参数
type Parameters<T> = T extends (...args: infer P) => any ? P : never;
type C = Parameters<(a: string, b: number) => void>; // [string, number]
// 推断 Promise 类型
type Awaited<T> = T extends Promise<infer U> ? U : T;
type D = Awaited<Promise<string>>; // string
type E = Awaited<Promise<number>>; // number
// 实际应用
function createPromise<T>(value: T): Promise<T> {
return Promise.resolve(value);
}
type PromiseType<T> = T extends Promise<infer U> ? U : never;
const promise = createPromise("hello");
type Result = PromiseType<typeof promise>; // string
实际应用模式
API 响应类型
typescript
// 通用 API 响应
interface ApiResponse<T> {
success: boolean;
data: T;
error?: string;
timestamp: number;
}
// 具体响应类型
type UserResponse = ApiResponse<User>;
type UsersResponse = ApiResponse<User[]>;
type ErrorResponse = ApiResponse<null>;
// API 函数
async function fetchUser(id: number): Promise<UserResponse> {
const response = await fetch(`/api/users/${id}`);
return response.json();
}
async function fetchUsers(): Promise<UsersResponse> {
const response = await fetch("/api/users");
return response.json();
}
分页类型
typescript
interface PaginatedResponse<T> {
data: T[];
pagination: {
page: number;
pageSize: number;
total: number;
totalPages: number;
};
}
type PaginatedUsers = PaginatedResponse<User>;
async function fetchUsers(
page: number = 1,
pageSize: number = 10
): Promise<PaginatedUsers> {
const response = await fetch(
`/api/users?page=${page}&pageSize=${pageSize}`
);
return response.json();
}
表单状态管理
typescript
interface FormField<T> {
value: T;
error: string | null;
touched: boolean;
dirty: boolean;
}
type FormState<T> = {
[K in keyof T]: FormField<T[K]>;
};
interface LoginForm {
username: string;
password: string;
rememberMe: boolean;
}
type LoginFormState = FormState<LoginForm>;
function createFormState<T>(initialValues: T): FormState<T> {
const state = {} as FormState<T>;
for (const key in initialValues) {
state[key] = {
value: initialValues[key],
error: null,
touched: false,
dirty: false
};
}
return state;
}
const loginFormState = createFormState<LoginForm>({
username: "",
password: "",
rememberMe: false
});
事件系统
typescript
// 事件类型映射
interface EventMap {
click: { x: number; y: number };
scroll: { top: number; left: number };
resize: { width: number; height: number };
}
// 事件监听器
class EventEmitter<T extends Record<string, any>> {
private listeners: {
[K in keyof T]?: Array<(event: T[K]) => void>;
} = {};
on<K extends keyof T>(event: K, listener: (event: T[K]) => void): void {
if (!this.listeners[event]) {
this.listeners[event] = [];
}
this.listeners[event]!.push(listener);
}
off<K extends keyof T>(event: K, listener: (event: T[K]) => void): void {
const listeners = this.listeners[event];
if (listeners) {
this.listeners[event] = listeners.filter(l => l !== listener);
}
}
emit<K extends keyof T>(event: K, data: T[K]): void {
const listeners = this.listeners[event];
if (listeners) {
listeners.forEach(listener => listener(data));
}
}
}
// 使用
const emitter = new EventEmitter<EventMap>();
emitter.on("click", (event) => {
console.log(`Clicked at ${event.x}, ${event.y}`);
});
emitter.on("resize", (event) => {
console.log(`Resized to ${event.width}x${event.height}`);
});
emitter.emit("click", { x: 10, y: 20 });
状态机
typescript
// 状态机类型
type StateMachine<S extends string, E extends string> = {
initial: S;
states: {
[K in S]: {
on?: {
[K2 in E]?: S;
};
entry?: () => void;
exit?: () => void;
};
};
};
// 创建状态机
function createStateMachine<S extends string, E extends string>(
config: StateMachine<S, E>
) {
let currentState = config.initial;
return {
getState: () => currentState,
send: (event: E) => {
const state = config.states[currentState];
const nextState = state.on?.[event];
if (nextState) {
state.exit?.();
currentState = nextState;
config.states[nextState].entry?.();
}
}
};
}
// 使用
type TrafficLightState = "red" | "yellow" | "green";
type TrafficLightEvent = "next" | "emergency";
const trafficLight = createStateMachine<TrafficLightState, TrafficLightEvent>({
initial: "red",
states: {
red: {
on: { next: "green" },
entry: () => console.log("Stop!")
},
green: {
on: { next: "yellow" },
entry: () => console.log("Go!")
},
yellow: {
on: { next: "red" },
entry: () => console.log("Caution!")
}
}
});
trafficLight.send("next"); // "Go!"
trafficLight.send("next"); // "Caution!"
最佳实践
1. 有意义的命名
typescript
// ❌ 不好:单字母
function process<T>(data: T): T { ... }
// ✅ 好:有意义的名称
function processData<TData>(data: TData): TData { ... }
// ✅ 好:描述约束
function findItem<TItem extends { id: number }>(
items: TItem[],
id: number
): TItem | undefined { ... }
2. 合理使用约束
typescript
// ❌ 过度约束
function process<T extends { a: number; b: string; c: boolean }>(obj: T) { ... }
// ✅ 合理约束
function process<T extends Record<string, unknown>>(obj: T) { ... }
// ✅ 精确约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] { ... }
3. 避免过度泛型
typescript
// ❌ 不必要
function add<T extends number>(a: T, b: T): number {
return a + b;
}
// ✅ 直接
function add(a: number, b: number): number {
return a + b;
}
4. 工具类型组合
typescript
// 组合使用
type UserUpdate = Partial<Omit<User, "id">> & { id: number };
// 实际应用
function updateUser(user: UserUpdate): User {
// ...
}
总结
核心概念
- 泛型函数:
function identity<T>(arg: T): T - 泛型约束:
T extends HasLength - 泛型接口/类:
interface Box<T> { ... } - 工具类型:
Partial<T>,Pick<T, K>,Record<K, V>
使用场景
- 通用函数:支持多种类型
- 类型安全的容器:Array、Map、Set
- API 响应:通用响应类型
- 状态管理:类型安全的状态
学习资源
延伸阅读: