第12章 类型编程深度
本章定位:深入TypeScript类型系统的核心,理解类型运算的本质,掌握
infer关键字、递归类型计算,以及类型级别的FizzBuzz等高级技巧。
12.1 TypeScript类型系统本质
Section titled “12.1 TypeScript类型系统本质”类型作为集合
Section titled “类型作为集合”TypeScript的类型本质上是一组值的集合:
// string类型 = 所有可能的字符串集合type StringSet = string; // "a", "b", "hello", ...
// number类型 = 所有可能的数字集合type NumberSet = number; // 1, 2, 3, 3.14, -5, ...
// never类型 = 空集合(没有任何值)type EmptySet = never;
// unknown类型 = 所有可能值的全集type UniversalSet = unknown;
// 联合类型 = 集合的并集type Union = string | number; // 所有字符串和所有数字的并集
// 交叉类型 = 集合的交集type Intersection = string & number; // never(没有值既是string又是number)结构类型系统
Section titled “结构类型系统”TypeScript使用结构类型(Structural Typing),只关心类型的结构,不关心类型的名称:
interface Point2D { x: number; y: number;}
interface Point2DAlias { x: number; y: number;}
// Point2D和Point2DAlias是同一类型const p: Point2D = { x: 0, y: 0 };const p2: Point2DAlias = p; // OK// unknown是所有类型的父类型type All = unknown;
// never是所有类型的子类型type Nothing = never;
// any是特殊的,既是所有类型的父类型也是子类型type Anything = any;TypeScript通过结构等价判断类型相等:
type A = { x: number };type B = { x: number };
// A和B是同一类型type Same = A extends B ? (B extends A ? true : false) : false;// true12.2 类型运算:交集、并集、差集
Section titled “12.2 类型运算:交集、并集、差集”集合运算在TypeScript中的实现
Section titled “集合运算在TypeScript中的实现”// 并集 (Union)type Union = string | number; // "a" | 1 | "b" | 2 | ...
// 交集 (Intersection)type Intersection = { name: string } & { age: number };// { name: string; age: number }
// 差集 - 使用Excludetype StringOrNumber = string | number;type NotString = Exclude<StringOrNumber, string>; // number
// 相对补集 - 使用Extracttype StringOrNumber2 = string | number | boolean;type OnlyNumber = Extract<StringOrNumber2, number | string>; // string | numbertype Difference<T, U> = T extends U ? never : T;
type A = "a" | "b" | "c";type B = "b" | "c";type Result = Difference<A, B>; // "a"type SymmetricDifference<T, U> = Difference<T, U> | Difference<U, T>;
type A = "a" | "b" | "c";type B = "b" | "c" | "d";type Result = SymmetricDifference<A, B>; // "a" | "d"type PowerSet<T extends string> = T extends any ? T | `${T},${PowerSet<Exclude<T, T>>}` : never;
type Result = PowerSet<"a" | "b">; // "a" | "b" | "a,b"12.3 infer关键字:类型推导
Section titled “12.3 infer关键字:类型推导”infer基本用法
Section titled “infer基本用法”infer在条件类型中声明一个类型变量:
// 从T中提取Promise resolve后的类型type Awaited<T> = T extends Promise<infer R> ? R : T;
type A = Awaited<Promise<string>>; // stringtype B = Awaited<number>; // numbertype C = Awaited<Promise<Promise<number>>>; // number(递归)提取函数返回类型
Section titled “提取函数返回类型”type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : never;
function createUser() { return { id: 1, name: "Alice" };}
type User = ReturnType<typeof createUser>; // { id: number; name: string }提取函数参数类型
Section titled “提取函数参数类型”type Parameters<T extends (...args: any) => any> = T extends (...args: infer P) => any ? P : never;
type A = Parameters<(name: string, age: number) => void>; // [string, number]提取构造函数参数
Section titled “提取构造函数参数”type ConstructorParameters<T extends new (...args: any) => any> = T extends new (...args: infer P) => any ? P : never;
class User { constructor( public name: string, public age: number ) {}}
type Params = ConstructorParameters<typeof User>; // [string, number]提取数组元素类型
Section titled “提取数组元素类型”type ElementType<T> = T extends (infer E)[] ? E : never;
type A = ElementType<string[]>; // stringtype B = ElementType<number[]>; // number提取对象属性类型
Section titled “提取对象属性类型”type PropertyType<T, K extends keyof T> = T extends { [P in K]: infer R } ? R : never;
interface User { name: string; age: number;}
type NameType = PropertyType<User, "name">; // stringinfer的递归应用
Section titled “infer的递归应用”// 深度展开Promisetype DeepAwaited<T> = T extends Promise<infer U> ? DeepAwaited<U> : T;
type A = DeepAwaited<Promise<Promise<Promise<string>>>>; // string12.4 递归类型计算
Section titled “12.4 递归类型计算”递归类型基础
Section titled “递归类型基础”类型可以引用自身:
// 链表interface ListNode<T> { value: T; next?: ListNode<T>;}
type StringList = ListNode<string>;// { value: string; next?: { value: string; next?: ... } }深度可选类型
Section titled “深度可选类型”type DeepPartial<T> = T extends object ? { [K in keyof T]?: DeepPartial<T[K]> } : T;
interface Config { server: { host: string; port: number; }; database: { credentials: { username: string; password: string; }; };}
type PartialConfig = DeepPartial<Config>;// { server?: { host?: string; port?: number; }; database?: { credentials?: { username?: string; password?: string; } } }深度只读类型
Section titled “深度只读类型”type DeepReadonly<T> = T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T;
type FrozenConfig = DeepReadonly<Config>;// 所有嵌套属性都是readonlyJSON类型
Section titled “JSON类型”type JSONPrimitive = string | number | boolean | null;type JSONValue = JSONPrimitive | JSONValue[] | { [key: string]: JSONValue };type JSONObject = { [key: string]: JSONValue };type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;
type A = Flatten<string[]>; // stringtype B = Flatten<string[][]>; // stringtype C = Flatten<string[][][]>; // stringtype D = Flatten<string | string[]>; // string深度Required
Section titled “深度Required”type DeepRequired<T> = T extends object ? { [K in keyof T]-?: DeepRequired<T[K]> } : T;12.5 类型级编程模式
Section titled “12.5 类型级编程模式”类型级别的FizzBuzz
Section titled “类型级别的FizzBuzz”type FizzBuzz<N extends number> = `${N}` extends `${infer _Fizz}${infer _Buzz}` ? _Fizz extends "" ? never : _Buzz extends "" ? never : `${_Fizz}${_Buzz}` : never;
// 或者更直接的实现type Mod3<N extends number> = N extends 3 | 6 | 9 ? true : false;type Mod5<N extends number> = N extends 5 | 10 ? true : false;
type FizzBuzz2<N extends number> = Mod3<N> extends true ? Mod5<N> extends true ? "FizzBuzz" : "Fizz" : Mod5<N> extends true ? "Buzz" : `${N}`;类型级的Prime Check
Section titled “类型级的Prime Check”type IsPrime<N extends number, T extends number[] = []> = N extends 1 | 4 | 6 | 8 | 9 | 10 ? false : N extends 2 | 3 | 5 | 7 ? true : T["length"] extends N ? true : N extends T["length"] ? false : IsPrime<N, [...T, never]>;
type A = IsPrime<2>; // truetype B = IsPrime<15>; // falsetype C = IsPrime<17>; // true类型级的Push/Pop
Section titled “类型级的Push/Pop”type Push<T extends any[], U> = [...T, U];
type A = Push<[1, 2], 3>; // [1, 2, 3]
type Pop<T extends any[]> = T extends [...infer Rest, infer Last] ? Rest : never;
type B = Pop<[1, 2, 3]>; // [1, 2]类型级的Map
Section titled “类型级的Map”type Map<T extends any[], F> = T extends [] ? [] : T extends [infer First, ...infer Rest] ? [F, ...Map<Rest, F>] : never;
type A = Map<[1, 2, 3], string>; // [string, string, string]
type Double<N extends number> = N;type B = Map<[1, 2, 3], Double<number>>; // [number, number, number]类型级的Filter
Section titled “类型级的Filter”type Filter<T extends any[], P> = T extends [] ? [] : T extends [infer First, ...infer Rest] ? First extends P ? [First, ...Filter<Rest, P>] : Filter<Rest, P> : never;
type A = Filter<[1, "a", 2, "b", 3], number>; // [1, 2, 3]12.6 类型级别的FizzBuzz
Section titled “12.6 类型级别的FizzBuzz”// 数字到字符串type ToString<N extends number> = `${N}`;
// FizzBuzz规则type FizzBuzz<N extends number> = [ToString<N>] extends [`${infer _Fizz}${infer _Buzz}`] ? _Fizz extends "" ? _Buzz extends "" ? ToString<N> : never : _Buzz extends "" ? never : "FizzBuzz" : "Fizz" | "Buzz" extends `${N}` ? never : `${N}`;
// 生成序列type Range<N extends number, R extends any[] = []> = R["length"] extends N ? R : Range<N, [...R, R["length"]]>;
type Sequence = Range<20>;// [0, 1, 2, ..., 19]
type FizzBuzzSequence = { [K in keyof Sequence]: FizzBuzz<Sequence[K] extends number ? Sequence[K] : never>;};// ["0", "1", "2", "Fizz", "4", "Buzz", "Fizz", "7", "8", "Fizz", "Buzz", "11", "Fizz", "13", "14", "FizzBuzz", ...]type FB<N extends number> = N extends 15 ? "FizzBuzz" : N extends 5 | 10 ? "Buzz" : N extends 3 | 6 | 9 ? "Fizz" : `${N}`;
type A = FB<1>; // "1"type B = FB<3>; // "Fizz"type C = FB<5>; // "Buzz"type D = FB<15>; // "FizzBuzz"12.7 类型安全API设计
Section titled “12.7 类型安全API设计”类型安全的API客户端
Section titled “类型安全的API客户端”// API端点定义interface API { "/users": { response: User[] }; "/users/:id": { params: { id: string }; response: User }; "/posts": { body: Post; response: Post };}
// 工具类型type ParseParams<T extends string> = T extends `${infer _}:${infer Param}/${infer Rest}` ? Param | ParseParams<`/${Rest}`> : T extends `${infer _}:${infer Param}` ? Param : never;
type ExtractParams<T extends string> = { [K in ParseParams<T>]: string;};
type GetResult<T extends string, API> = T extends keyof API ? API[T]["response"] : never;
// 使用async function apiGet<T extends keyof API>( endpoint: T, params?: ExtractParams<T>): Promise<GetResult<T, API>> { // 实现 return {} as any;}
// 自动补全和类型检查const users = await apiGet("/users");const user = await apiGet("/users/:id", { id: "123" });链式调用类型
Section titled “链式调用类型”type Method = "GET" | "POST" | "PUT" | "DELETE";type Entity = "users" | "posts" | "comments";
type Chain = `${Method} /${Entity}`;
type Routes = { [K in Chain]: () => Promise<any>;};
class API { private routes: Routes = {} as Routes;
route<K extends Chain>(chain: K): Routes[K] { return this.routes[chain]; }
// 链式方法 get(entity: Entity): this { // 实现 return this; }}
const api = new API();api.get("users").execute();类型安全的EventEmitter
Section titled “类型安全的EventEmitter”type EventMap = { "user:created": { id: string; name: string }; "user:deleted": { id: string }; "post:published": { id: string; title: string };};
class TypedEmitter<T extends Record<string, any>> { private listeners: Partial<{ [K in keyof T]: Set<(data: T[K]) => void> }> = {};
on<K extends keyof T>(event: K, listener: (data: T[K]) => void): this { if (!this.listeners[event]) { this.listeners[event] = new Set(); } this.listeners[event]!.add(listener); return this; }
emit<K extends keyof T>(event: K, data: T[K]): void { this.listeners[event]?.forEach(listener => listener(data)); }}
const emitter = new TypedEmitter<EventMap>();
emitter.on("user:created", (data) => { console.log(data.id, data.name); // 类型安全!});
emitter.emit("user:created", { id: "123", name: "Alice" });12.8 本章小结
Section titled “12.8 本章小结”本章深入了TypeScript类型编程的深度:
-
类型系统本质:
- 类型是值的集合
- 结构类型系统
- unknown是全集,never是空集
-
类型运算:
- 并集(
|)、交集(&) - 差集(Exclude)、对称差集
- 集合操作在类型中的应用
- 并集(
-
infer关键字:
- 在条件类型中声明类型变量
- 提取函数返回类型、参数类型
- 提取数组元素、对象属性
-
递归类型:
- 类型可以引用自身
- 实现DeepPartial、DeepReadonly
- 深度展开类型
-
类型级编程:
- 编译时计算
- FizzBuzz、Prime Check
- Push/Pop、Map、Filter
-
类型安全API:
- 类型安全的API客户端
- 链式调用设计
- TypedEventEmitter
练习12.1:类型运算
Section titled “练习12.1:类型运算”实现Intersection<T, U>计算两个对象类型的交集。
练习12.2:infer应用
Section titled “练习12.2:infer应用”实现First<T>提取数组第一个元素的类型。
练习12.3:递归类型
Section titled “练习12.3:递归类型”实现DeepRequired<T>将嵌套属性都设为必填。
练习12.4:类型级排序
Section titled “练习12.4:类型级排序”实现Sort<T>在类型级别排序数字数组。
练习12.5:类型安全路由
Section titled “练习12.5:类型安全路由”设计一个类型安全的路由系统。
下一章我们将学习TypeScript的运行时类型检查,了解如何使用zod、yup等库进行运行时验证。