TypeScript 高级类型体操
TypeScript 的类型系统是图灵完备的——这意味着你可以用类型做很多你想不到的事。
条件类型
type IsArray<T> = T extends any[] ? true : false;
type A = IsArray<number[]>; // true
type B = IsArray<string>; // false
infer:从类型中提取
// 提取函数返回值类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
// 提取 Promise 内部类型
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
// 提取数组元素类型
type ElementType<T> = T extends (infer E)[] ? E : never;
// 提取函数第一个参数
type FirstArg<T> = T extends (first: infer F, ...rest: any[]) => any ? F : never;
模板字面量类型
type EventName<T extends string> = `on${Capitalize<T>}`;
type ClickEvent = EventName<'click'>; // "onClick"
type ChangeEvent = EventName<'change'>; // "onChange"
// 实用工具:将对象 key 转为 camelCase getter
type Getters<T> = {
[K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
}
interface User { name: string; age: number; }
type UserGetters = Getters<User>;
// { getName: () => string; getAge: () => number; }
递归类型
// 深度 Readonly
type DeepReadonly<T> = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
}
// 深度 Partial
type DeepPartial<T> = {
[K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K];
}
实际应用:类型安全的事件系统
interface Events {
'user:login': { userId: string; timestamp: number };
'user:logout': { userId: string };
'post:created': { postId: string; title: string };
}
class TypedEventEmitter<T extends Record<string, any>> {
on<K extends keyof T>(event: K, handler: (data: T[K]) => void): void { /* ... */ }
emit<K extends keyof T>(event: K, data: T[K]): void { /* ... */ }
}
const emitter = new TypedEventEmitter<Events>();
emitter.on('user:login', ({ userId, timestamp }) => {
// userId 和 timestamp 都有完整类型提示
console.log(userId, timestamp);
});
TypeScript 的类型系统越深入,就越能感受到它的精妙。类型即文档,类型即约束。