TypeScript的类型系统是前端工程化的核心能力,而泛型则是类型系统中最难掌握的部分。本文通过实际的工具类型封装案例,讲解条件类型、映射类型、模板字面量类型等高级类型特性的使用方式,并展示如何用泛型约束构建类型安全的业务代码。
TypeScript泛型基础与约束场景
泛型解决的是类型与具体值解耦的问题。先看一个基础示例,泛型函数获取对象属性:
function getProp<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "张三", age: 30 };
const name = getProp(user, "name"); // 类型推断为 string
const age = getProp(user, "age"); // 类型推断为 number
K extends keyof T表示第二个参数必须是对象的合法键名,编译器在调用时就能拦截拼写错误的键,这是泛型约束最常见的用途之一。
映射类型与类型工具封装实战
映射类型可以在已有类型基础上生成新类型,TypeScript内置的Partial、Required、Pick都是映射类型的实例。业务开发中经常需要把接口中的可选字段与必选字段分开处理,可以用条件类型组合实现:
type RequiredKeys<T> = {
[K in keyof T]-?: T[K] extends undefined ? never : K
}[keyof T];
type OptionalKeys<T> = Exclude<keyof T, RequiredKeys<T>>;
interface FormState {
name: string;
age?: number;
email?: string;
}
// RequiredKeys = "name"
// OptionalKeys = "age" | "email"
-? 运算符移除可选标记,extends undefined判断字段是否允许为空,never被排除后剩下的键就是必选字段。
模板字面量类型提升API类型安全
模板字面量类型把字符串字面量与联合类型组合,让接口参数在编译期就受限。典型应用是事件监听器与路由参数的类型化:
type EventName = `on${Capitalize<"click" | "input" | "change">}`;
// 得到 "onClick" | "onInput" | "onChange"
type RouteParams = "user" | "post" | "comment";
type RoutePath = `/api/${RouteParams}/${number}`;
// 得到 "/api/user/123" 形式的字面量类型
function request(path: RoutePath) { /* ... */ }
request("/api/user/123"); // 合法
request("/api/admin/123"); // 编译报错
路由参数全部类型化之后,前端请求错误在编译期就能暴露,不需要到运行时用正则校验。
泛型约束在业务代码中的实践技巧
实际项目中泛型常与请求封装、状态管理结合。以一个请求封装为例,让返回类型跟随接口变化:
async function apiGet<T>(url: string, config?: RequestInit): Promise<T> {
const res = await fetch(url, config);
if (!res.ok) throw new Error(`请求失败: ${res.status}`);
return res.json() as T;
}
interface UserDTO { id: number; name: string; }
const user = await apiGet<UserDTO>("/api/user/1");
// user 类型自动推断为 UserDTO
建议在团队代码规范里要求所有接口调用必须带泛型参数,配合ESLint的no-explicit-any规则,可以显著减少类型报错。泛型写好后用TypeScript官方测试框架做类型断言,防止重构时类型逻辑被破坏。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/typescript-fan-xing-shi-zhan-gao-ji-lei-xing-gong-ju-feng/