为什么需要高级类型
TypeScript 的类型系统是图灵完备的。这意味着你可以在类型层面进行复杂的计算和逻辑推理。高级类型能帮助我们:
- 捕获更多运行时错误
- 提供更好的 IDE 自动补全
- 让 API 设计更具表达力
条件类型
条件类型类似于三元表达式,但运作在类型层面:
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<42>; // false
infer 关键字
infer 让我们从类型中提取信息:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
type UnpackPromise<T> = T extends Promise<infer U> ? U : T;
type Result = UnpackPromise<Promise<string>>; // string
映射类型
映射类型可以基于已有类型创建新类型:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
type Partial<T> = {
[P in keyof T]?: T[P];
};
// 将所有属性变为可选且 nullable
type Nullable<T> = {
[P in keyof T]: T[P] | null;
};
模板字面量类型
这是 TypeScript 4.1 引入的强大特性:
type EventType = "click" | "hover" | "focus";
type EventHandler = `on${Capitalize<EventType>}`;
// "onClick" | "onHover" | "onFocus"
// 获取对象中所有字符串值的键
type StringKeys<T> = {
[K in keyof T]: T[K] extends string ? K : never;
}[keyof T];
实战:类型安全的路由参数
type ExtractParams<T extends string> =
T extends `${infer _Start}/:${infer Param}/${infer Rest}`
? { [K in Param | keyof ExtractParams<`/${Rest}`>]: string }
: T extends `${infer _Start}/:${infer Param}`
? { [K in Param]: string }
: {};
// 自动推断路由参数类型
const route = "/users/:userId/posts/:postId" as const;
type Params = ExtractParams<typeof route>;
// { userId: string; postId: string }
总结
TypeScript 高级类型的核心思想:
- 条件类型 — 类型层面的 if/else
- infer — 从类型中提取信息
- 映射类型 — 批量转换类型成员
- 模板字面量 — 字符串层面的类型操作
这些技巧组合起来,可以实现极其强大的类型安全抽象。