TypeScript 泛型
泛型(Generics)是创建可复用组件的核心特性,允许组件支持多种类型而不是单一类型。
泛型基础
为什么需要泛型
// 不使用泛型 - 类型丢失
function identity(arg: any): any {
return arg;
}
const result = identity("hello"); // result 类型是 any
// 使用泛型 - 保留类型
function identityGeneric<T>(arg: T): T {
return arg;
}
const resultStr = identityGeneric("hello"); // resultStr 类型是 string
const resultNum = identityGeneric(123); // resultNum 类型是 number
解释:泛型 T 是一个类型变量,在调用时由传入的参数类型决定。这样可以保留输入和输出的类型关系。
泛型函数
// 基本语法
function identity<T>(arg: T): T {
return arg;
}
// 调用方式一:类型推断
let output1 = identity("hello"); // string
// 调用方式二:显式指定类型
let output2 = identity<string>("hello"); // string
// 箭头函数写法
const identityArrow = <T>(arg: T): T => arg;
// 在 JSX 中需要使用 extends 避免与 JSX 标签混淆
const identityJSX = <T extends unknown>(arg: T): T => arg;
泛型接口
定义泛型接口
// 泛型接口
interface Container<T> {
value: T;
getValue(): T;
setValue(value: T): void;
}
// 实现
const numberContainer: Container<number> = {
value: 123,
getValue() { return this.value; },
setValue(value) { this.value = value; }
};
const stringContainer: Container<string> = {
value: "hello",
getValue() { return this.value; },
setValue(value) { this.value = value; }
};
函数类型泛型接口
interface GenericFunction<T> {
(arg: T): T;
}
// 使用
const myFunction: GenericFunction<number> = (arg) => arg * 2;
myFunction(5); // 10
泛型类
基本泛型类
class Box<T> {
private content: T;
constructor(content: T) {
this.content = content;
}
getContent(): T {
return this.content;
}
setContent(content: T): void {
this.content = content;
}
}
// 使用
const numberBox = new Box<number>(123);
console.log(numberBox.getContent()); // 123
const stringBox = new Box<string>("hello");
console.log(stringBox.getContent()); // "hello"
泛型约束类
interface Comparable {
compareTo(other: this): number;
}
class SortedList<T extends Comparable> {
private items: T[] = [];
add(item: T): void {
// 按顺序插入
let i = 0;
while (i < this.items.length && this.items[i].compareTo(item) < 0) {
i++;
}
this.items.splice(i, 0, item);
}
getItems(): T[] {
return this.items;
}
}
泛型约束
基本约束
// 约束 T 必须有 length 属性
interface Lengthwise {
length: number;
}
function logLength<T extends Lengthwise>(arg: T): T {
console.log(arg.length);
return arg;
}
logLength("hello"); // 5
logLength([1, 2, 3, 4, 5]); // 5
logLength({ length: 10 }); // 10
// logLength(123); // 错误:number 没有 length 属性
解释:使用 extends 关键字约束泛型类型必须满足某些条件。
在泛型约束中使用类型参数
// 获取对象的某个属性
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = {
name: "张三",
age: 25,
city: "北京"
};
const name = getProperty(user, "name"); // string
const age = getProperty(user, "age"); // number
// getProperty(user, "gender"); // 错误:不存在 "gender" 属性
使用类类型约束
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
}
// 约束 T 必须是 Animal 或其子类
function createInstance<T extends Animal>(ctor: new (...args: any[]) => T, name: string): T {
return new ctor(name);
}
const dog = createInstance(Dog, "旺财");
console.log(dog.name); // "旺财"
多类型参数
多个泛型参数
// 多个类型参数
function pair<K, V>(key: K, value: V): [K, V] {
return [key, value];
}
const p1 = pair("name", "张三"); // [string, string]
const p2 = pair(1, "first"); // [number, string]
const p3 = pair(true, 100); // [boolean, number]
// 对象映射
function mapObject<K extends string, V, R>(
obj: Record<K, V>,
mapper: (value: V, key: K) => R
): Record<K, R> {
const result = {} as Record<K, R>;
for (const key in obj) {
result[key] = mapper(obj[key], key);
}
return result;
}
const numbers = { a: 1, b: 2, c: 3 };
const doubled = mapObject(numbers, (v) => v * 2);
// { a: 2, b: 4, c: 6 }
泛型默认类型
// 默认类型
interface ApiResponse<T = any> {
code: number;
message: string;
data: T;
}
// 使用默认类型
const response1: ApiResponse = {
code: 200,
message: "success",
data: { name: "张三" }
};
// 指定类型
const response2: ApiResponse<string> = {
code: 200,
message: "success",
data: "hello"
};
泛型工具类型
Partial
将类型 T 的所有属性变为可选:
interface User {
name: string;
age: number;
email: string;
}
function updateUser(user: User, updates: Partial<User>): User {
return { ...user, ...updates };
}
const user: User = { name: "张三", age: 25, email: "[email protected]" };
const updated = updateUser(user, { age: 26 });
// { name: "张三", age: 26, email: "[email protected]" }
Required
将类型 T 的所有属性变为必需:
interface Props {
name?: string;
age?: number;
}
const props: Required<Props> = {
name: "张三",
age: 25
// 都必须提供
};
Readonly
将类型 T 的所有属性变为只读:
interface Point {
x: number;
y: number;
}
const point: Readonly<Point> = { x: 10, y: 20 };
// point.x = 15; // 错误:只读属性
Pick
从类型 T 中选取部分属性:
interface User {
id: number;
name: string;
email: string;
password: string;
}
// 只选取 id 和 name
type UserPreview = Pick<User, "id" | "name">;
// { id: number; name: string; }
Omit
从类型 T 中排除部分属性:
interface User {
id: number;
name: string;
email: string;
password: string;
}
// 排除 password
type PublicUser = Omit<User, "password">;
// { id: number; name: string; email: string; }
Record
创建键类型为 K,值类型为 T 的对象类型:
type Role = "admin" | "user" | "guest";
const permissions: Record<Role, string[]> = {
admin: ["read", "write", "delete"],
user: ["read", "write"],
guest: ["read"]
};
ReturnType
获取函数类型的返回类型:
function getUser() {
return { name: "张三", age: 25 };
}
type User = ReturnType<typeof getUser>;
// { name: string; age: number; }
Parameters
获取函数类型的参数类型:
function greet(name: string, age: number): string {
return `Hello, ${name}!`;
}
type GreetParams = Parameters<typeof greet>;
// [string, number]
泛型实战示例
通用 API 客户端
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
class ApiClient {
async get<T>(url: string): Promise<ApiResponse<T>> {
const response = await fetch(url);
return response.json();
}
async post<T, D>(url: string, data: D): Promise<ApiResponse<T>> {
const response = await fetch(url, {
method: "POST",
body: JSON.stringify(data)
});
return response.json();
}
}
// 使用
const client = new ApiClient();
const users = await client.get<User[]>("/api/users");
const newUser = await client.post<User, CreateUserDto>("/api/users", {
name: "张三",
email: "[email protected]"
});
类型安全的事件发射器
type EventHandler<T = any> = (data: T) => void;
class EventEmitter<EventMap extends Record<string, any>> {
private handlers: Partial<{ [K in keyof EventMap]: EventHandler<EventMap[K]>[] }> = {};
on<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): void {
if (!this.handlers[event]) {
this.handlers[event] = [];
}
this.handlers[event]!.push(handler);
}
emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void {
const handlers = this.handlers[event];
if (handlers) {
handlers.forEach(handler => handler(data));
}
}
off<K extends keyof EventMap>(event: K, handler: EventHandler<EventMap[K]>): void {
const handlers = this.handlers[event];
if (handlers) {
const index = handlers.indexOf(handler);
if (index > -1) {
handlers.splice(index, 1);
}
}
}
}
// 使用
interface MyEvents {
userCreated: { id: number; name: string };
userDeleted: { id: number };
}
const emitter = new EventEmitter<MyEvents>();
emitter.on("userCreated", (data) => {
console.log(`User created: ${data.name}`);
});
emitter.emit("userCreated", { id: 1, name: "张三" });
小结
本章我们学习了 TypeScript 泛型:
- 泛型函数和类型推断
- 泛型接口和泛型类
- 泛型约束
- 多类型参数
- 泛型默认类型
- 内置工具类型
- 泛型实战应用
练习
- 实现一个泛型函数
first,返回数组的第一个元素 - 创建一个泛型
Stack类 - 实现一个类型安全的
Object.keys包装函数 - 使用泛型实现一个简单的依赖注入容器