📖 核心概念
- 泛型基础与约束
- 条件类型与infer
- 映射类型与键重映射
- 模板字面量类型
- 递归类型与深层只读
- 常用工具类型实现
💻 代码实现
泛型与条件类型
✅ typescript
// 泛型约束
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// 条件类型
type IsString<T> = T extends string ? true : false;
type A = IsString<string>; // true
type B = IsString<number>; // false
// infer - 在条件类型中推断类型
type ReturnTypeOf<T> = T extends (...args: any[]) => infer R ? R : never;
type PromiseValue<T> = T extends Promise<infer V> ? V : T;
// 递归展开Promise
type Awaited<T> = T extends Promise<infer V> ? Awaited<V> : T;
type DeepPromise = Promise<Promise<Promise<string>>>;
type Result = Awaited<DeepPromise>; // string
// 实用条件类型
type FunctionParams<T> = T extends (...args: infer P) => any ? P : never;
type ConstructorParams<T> = T extends new (...args: infer P) => any ? P : never;
// 分布式条件类型
type ToArray<T> = T extends any ? T[] : never;
type StrOrNumArr = ToArray<string | number>; // string[] | number[]
// 阻止分布式:用元组包裹
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type StrOrNumArr2 = ToArrayNonDist<string | number>; // (string | number)[]
映射类型与类型体操
✅ typescript
// 映射类型
type Readonly<T> = { readonly [K in keyof T]: T[K] };
type Partial<T> = { [K in keyof T]?: T[K] };
type Required<T> = { [K in keyof T]-?: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };
// 键重映射 (TS 4.1+)
type Getters<T> = {
[K in keyof T as \`get\${Capitalize<string & K>}\`]: () => T[K];
};
interface Person { name: string; age: number; }
type PersonGetters = Getters<Person>;
// { getName: () => string; getAge: () => number }
// 模板字面量类型
type EventName<T extends string> = \`on\${Capitalize<T>}\`;
type ClickEvent = EventName<'click'>; // "onClick"
// 深层只读
type DeepReadonly<T> = T extends Function
? T
: T extends object
? { readonly [K in keyof T]: DeepReadonly<T[K]> }
: T;
// 深层Partial
type DeepPartial<T> = T extends Function
? T
: T extends Array<infer U>
? Array<DeepPartial<U>>
: T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: 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 }
: {};
type RouteParams = ExtractParams<'/users/:userId/posts/:postId'>;
// { userId: string; postId: string }
// 类型安全的事件系统
type EventMap = {
login: { userId: string; timestamp: Date };
logout: { userId: string };
error: { code: number; message: string };
};
type EventHandler<T extends keyof EventMap> = (payload: EventMap[T]) => void;
class TypeSafeEmitter<T extends Record<string, any>> {
private handlers = new Map<keyof T, Set<Function>>();
on<K extends keyof T>(event: K, handler: (payload: T[K]) => void) {
if (!this.handlers.has(event)) this.handlers.set(event, new Set());
this.handlers.get(event)!.add(handler);
return () => this.off(event, handler);
}
off<K extends keyof T>(event: K, handler: (payload: T[K]) => void) {
this.handlers.get(event)?.delete(handler);
}
emit<K extends keyof T>(event: K, payload: T[K]) {
this.handlers.get(event)?.forEach(h => h(payload));
}
}
const emitter = new TypeSafeEmitter<EventMap>();
emitter.on('login', ({ userId }) => { /* TypeScript自动推断类型 */ });
emitter.emit('error', { code: 404, message: 'Not found' });
🔍 类型体操实战技巧
类型体操的核心思路:先用具体类型推导,再泛化。善用infer在条件类型中提取类型、使用递归类型处理嵌套结构、利用模板字面量类型处理字符串模式。推荐练习Type Challenges题库,从Easy到Hard逐步提升。
🎯 练习任务
- 1 实现DeepMerge类型,深度合并两个对象类型
- 2 创建一个类型安全的路由匹配器
- 3 实现JSON类型的TypeScript表示
- 4 编写一个将camelCase转为snake_case的模板字面量类型
🏆 成就解锁
类型体操冠军 — 掌握TypeScript高级类型编程,让类型系统为你工作