01. 泛型——类型的变量
泛型就是把类型也当成参数——你可以写一个函数、类或接口,不指定具体的类型,而是留一个类型变量(T、U 等),使用者调用时再填具体类型。
为什么需要泛型?假设你写了一个 identity 函数——传什么返回什么。不用泛型的话只能用 any,丢了类型安全。用泛型:identity<string>('hello') 返回 string,identity<number>(42) 返回 number。
泛型变量通常用 T、U、V 表示,多个泛型参数逗号分隔 <T, U>。T 是 Type 的缩写,也可以用有意义的名字如 <TData, TError>。
泛型不是运行时存在的——编译后会被擦除。它纯粹是编译时的类型检查工具。
typescript
// 不用泛型——丢了类型信息
function identityAny(arg: any): any {
return arg;
}
get result = identityAny('hello'); // result: any
// 用泛型——保留类型信息
function identity<T>(arg: T): T {
return arg;
}
get result2 = identity('hello'); // result2: string (自动推断)
get result3 = identity<number>(42); // result3: number (显式指定)泛型参数大部分情况可以自动推断,不需要显式写 <Type>。TS 根据传入参数的类型自动推导 T。只在推断不出来或需要强制类型时才显式写。
02. 泛型约束——extends 限制类型范围
泛型不加限制的话 T 可以是任何类型——但你如果用到了 .length 属性,不是所有类型都有 .length。这时需要泛型约束。
用 extends 约束泛型:<T extends { length: number }> 表示 T 必须是有 length 属性的类型(string、Array、NodeList 等)。
泛型约束的另一个常用场景——<T extends keyof SomeType> 确保 T 是某个类型的属性名:function getProperty<T, K extends keyof T>(obj: T, key: K)。
多个约束可以用交叉类型:<T extends A & B>。
还有个实用的泛型默认值:<T = string> 不传泛型参数时默认为 string。
typescript
// 泛型约束——要求有 length 属性
function logLength<T extends { length: number }>(item: T): T {
console.log(item.length);
return item;
}
logLength('hello'); // OK——string 有 length
logLength([1, 2, 3]); // OK——数组有 length
// logLength(123); // 报错——number 没有 length
// 约束为某个类型的键
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// 泛型默认值
function createList<T = string>(): T[] {
return [];
}
get list = createList(); // list: string[]
get list2 = createList<number>(); // list2: number[]03. 泛型在类与接口中的使用
泛型最经典的应用场景:
Repository 模式——数据库操作封装。class Repository<T> 对任何实体都能提供 findById、save、delete 操作。T 是实体类型。
Result/Option 类型——Rust 风格的错误处理。type Result<T, E> = { ok: true; value: T } | { ok: false; error: E }。
API 响应包装——interface ApiResponse<T> { code: number; data: T; message: string }。不同接口返回不同 data 类型。
Event Emitter——class EventEmitter<T extends Record<string, any>>。T 是事件名到参数的映射。
typescript
// Repository 模式
class Repository<T extends { id: number }> {
private items: T[] = [];
findById(id: number): T | undefined {
return this.items.find(item => item.id === id);
}
save(item: T): void {
this.items.push(item);
}
}
interface User { id: number; name: string; }
get userRepo = new Repository<User>();
// API 响应包装
interface ApiResponse<T> {
code: number;
data: T;
message: string;
}
type UserResponse = ApiResponse<User>;
type ListResponse = ApiResponse<User[]>;04. 泛型的高级模式——条件泛型
泛型跟条件类型结合能做很酷的事:
根据泛型参数的值返回不同类型——T extends 'string' ? string : T extends 'number' ? number : never。
泛型参数可以是另一个泛型——<T extends Array<infer U>> 提取数组元素的类型。
模板字面量类型的泛型——TS 4.1+ 支持字符串模板作为类型:type EventName<T extends string> = `on${Capitalize<T>}`。type ChangeEvent = EventName<'change'>; // 'onChange'。
这些高级泛型模式是类型体操的范畴,日常业务开发用到的少,但写通用工具库时会频繁用到。
typescript
// 条件泛型——根据输入类型返回不同输出
interface StringMap { string: string; number: number; boolean: boolean; }
type GetType<T extends keyof StringMap> = StringMap[T];
type A = GetType<'string'>; // string
get B = GetType<'number'>; // number
// 提取 Promise 的值类型
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type P1 = Unwrap<Promise<string>>; // string
type P2 = Unwrap<number>; // number
// 模板字面量泛型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickHandler = EventName<'click'>; // 'onClick'05. 日常开发中最常用的泛型模式
不用记所有高级泛型,日常开发 90% 就这几个模式:
1. 带泛型的函数——useState<T>、Array<T>、Promise<T>。TypeScript 自带,不需要自己写。
2. 泛型约束——确保传入的对象有某个属性,确保参数是对象的合法 key。
3. 泛型响应包装——所有 API 调用的统一返回格式。
4. 泛型工具类型——Partial、Pick、Omit、Record。四个组合能解决大部分对象类型变换。
5. React 里的泛型——useState<User>()、useRef<HTMLInputElement>(null)、forwardRef<HTMLDivElement>。
typescript
// 日常泛型模式
// 1. 带泛型的 hooks
const [user, setUser] = useState<User | null>(null);
const ref = useRef<HTMLInputElement>(null);
// 2. 泛型响应
async function fetchData<T>(url: string): Promise<ApiResponse<T>> {
const res = await fetch(url);
return res.json();
}
// 3. 泛型事件处理
function handleChange<T extends HTMLInputElement>(e: React.ChangeEvent<T>) {
console.log(e.target.value);
}
// 4. 泛型回调
function debounce<T extends (...args: any[]) => any>(
fn: T, delay: number
): (...args: Parameters<T>) => void {
let timer: ReturnType<typeof setTimeout>;
return (...args) => {
clearTimeout(timer);
timer = setTimeout(() => fn(...args), delay);
};
}知识测验
第 1/5 题正确 0
泛型的主要作用是什么?
下一节
下一节 高级类型