TypeScript泛型基础与约束机制
TypeScript的泛型是实现前端工程化中组件复用和类型安全的核心工具。泛型允许在定义函数、接口和类时使用类型参数,在实际调用时传入具体类型。泛型约束通过extends关键字限制类型参数的范围,确保传入的类型满足特定条件。
// 基础泛型函数
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
const user = { name: "张三", age: 30, email: "zhangsan@example.com" };
const userName = getProperty(user, "name"); // 类型推断为 string
const userAge = getProperty(user, "age"); // 类型推断为 number
// getProperty(user, "phone"); // 编译报错
// 泛型约束:限制类型参数必须有特定属性
interface HasLength {
length: number;
}
function logLength<T extends HasLength>(value: T): T {
console.log(value.length);
return value;
}
logLength("hello"); // string 有 length 属性
logLength([1, 2, 3]); // array 有 length 属性
// logLength(123); // 编译报错:number 没有 length 属性
keyof T是TypeScript类型系统中最常用的操作符之一,它提取类型T的所有属性名组成联合类型。与泛型结合使用可以在编译阶段捕获属性名拼写错误,避免运行时的undefined问题。
条件类型与分布式条件类型
条件类型是TypeScript类型体操的基石,语法类似三元表达式:T extends U ? X : Y。当条件类型作用于联合类型时,会自动分发到每个成员,这种特性称为分布式条件类型。
// 基础条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<42>; // false
type C = IsString<string | number>; // true | false(分布式)
// Exclude和Extract的实现原理
type MyExclude<T, U> = T extends U ? never : T;
type MyExtract<T, U> = T extends U ? T : never;
type T1 = MyExclude<"a" | "b" | "c", "a">; // "b" | "c"
type T2 = MyExtract<"a" | "b" | "c", "a" | "b">; // "a" | "b"
// infer关键字:在条件类型中提取类型变量
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 获取Promise的泛型参数
type Unpacked<T> = T extends Promise<infer U> ? U : T;
type P = Unpacked<Promise<number>>; // number
type Q = Unpacked<string>; // string
// 获取数组元素类型
type ElementOf<T> = T extends (infer E)[] ? E : never;
type Arr = ElementOf<string[]>; // string
type Arr2 = ElementOf<number[]>; // number
infer关键字在条件类型的extends子句中声明一个类型变量,当条件成立时该变量绑定到实际类型。这是实现ReturnType、Parameters、ConstructorParameters等内置工具类型的底层机制。
映射类型与模板字面量类型
映射类型通过遍历现有类型的属性集生成新类型。模板字面量类型利用字符串模板语法创建类型约束,在构建类型安全的API路径和事件系统中非常实用。
// 映射类型:将所有属性变为只读
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
// 将所有属性变为可选
type Optional<T> = {
[P in keyof T]?: T[P];
};
// 模板字面量类型:构建类型安全的事件系统
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<"click">; // "onClick"
type FocusEvent = EventName<"focus">; // "onFocus"
// API路由路径类型
type ApiPath = `/api/v${number}/${string}`;
const validPath: ApiPath = "/api/v1/users"; // 正确
// const invalidPath: ApiPath = "/api/users"; // 编译报错:缺少版本号
// 对象路径类型安全
type Path<T> = T extends object
? {
[K in keyof T & string]: K | `${K}.${Path<T[K]>}`
}[keyof T & string]
: never;
interface Config {
database: { host: string; port: number };
cache: { ttl: number };
}
type ConfigPath = Path<Config>;
// "database" | "database.host" | "database.port" | "cache" | "cache.ttl"
Capitalize是TypeScript内置的类型工具,将字符串首字母大写。模板字面量类型与映射类型、条件类型组合使用,可以实现编译期路径校验,在对象嵌套访问时提供类型安全的路径字符串。
实际项目中的泛型工具类型设计
在Vue3和React项目中,类型体操的实际应用场景包括组件Props类型推导、API响应类型映射和表单类型安全。以下是一些工程实践中常用的工具类型:
// 1. 深层Partial:递归地将所有属性变为可选
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 2. 深层Readonly
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 3. 联合类型转交叉类型
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void)
? I
: never;
// 4. API响应类型包装
interface ApiResponse<T> {
code: number;
message: string;
data: T;
}
// 5. Vue3组合式函数类型推导
function useFetch<T>(url: string) {
const data = ref<T | null>(null) as Ref<T | null>;
const error = ref<Error | null>(null);
const loading = ref(false);
const execute = async () => {
loading.value = true;
try {
const response = await fetch(url);
const json: ApiResponse<T> = await response.json();
data.value = json.data;
} catch (e) {
error.value = e as Error;
} finally {
loading.value = false;
}
};
return { data, error, loading, execute };
}
// 使用时自动推断T的类型
const { data: users } = useFetch<User[]>("/api/users");
// users.value 的类型为 User[] | null
DeepPartial在表单编辑场景中用于部分更新对象字段,避免要求传入完整对象。UnionToIntersection在Mixin模式中将多个对象的类型合并为交叉类型,实现类型安全的多继承效果。
TypeScript类型系统的核心价值在于编译期错误捕获,而非运行时行为变更。合理运用泛型约束和条件类型可以在不牺牲运行性能的前提下大幅提升代码的可维护性。实际项目中应避免过度设计:简单场景用基础泛型即可,类型体操的进阶技巧留给框架库和公共工具函数。当类型推导变得过于复杂时,考虑通过显式类型注解或类型断言简化代码,可读性优先于极致的类型安全。
原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/typescript-lei-xing-ti-cao-shi-zhan-fan-xing-yue-shu-yu/