TypeScript用了很久,但很多人还停留在”给变量加个类型注解”的阶段。其实TS的类型系统非常强大,掌握条件类型、映射类型、模板字面量类型等高级特性,能写出类型安全的工具函数和库,让IDE提示更精准,bug更少。本文从实用角度讲解TS高级类型的核心技巧。
为什么要学TypeScript高级类型
TypeScript已经成为前端开发的标配,但很多人只用到了基础类型注解。掌握高级类型编程,能让你的代码更安全、更智能,IDE提示更精准,还能写出类型安全的工具函数和库。本文从实用角度讲解TypeScript高级类型的核心技巧。
条件类型与infer
条件类型是TS类型编程的基础,语法类似三元表达式:
type IsString = T extends string ? true : false;
type A = IsString; // true
type B = IsString; // false
infer关键字用于在条件类型中提取类型:
// 提取函数返回值类型
type ReturnType = T extends (...args: any[]) => infer R ? R : never;
type Fn = (a: number, b: string) => boolean;
type R = ReturnType; // boolean
// 提取Promise内部类型
type UnwrapPromise = T extends Promise ? U : T;
type P = UnwrapPromise<Promise>; // User
映射类型
映射类型可以基于已有类型创建新类型:
// 所有属性变为可选
type Partial = {
[K in keyof T]?: T[K];
};
// 所有属性变为只读
type Readonly = {
readonly [K in keyof T]: T[K];
};
// 自定义:所有属性变为可空
type Nullable = {
[K in keyof T]: T[K] | null;
};
// 带as重映射:排除特定key
type RemoveId = {
[K in keyof T as K extends 'id' ? never : K]: T[K];
};
模板字面量类型
TS 4.1引入的模板字面量类型,可以操作字符串类型:
type EventName = `on${Capitalize}`;
type ClickEvent = EventName; // 'onClick'
// 为对象的每个属性生成getter/setter
type Getters = {
[K in keyof T as `get${Capitalize}`]: () => T[K];
};
interface User { name: string; age: number; }
type UserGetters = Getters;
// { getName: () => string; getAge: () => number; }
实用类型工具
| 工具类型 | 作用 | 示例 |
| Partial<T> | 所有属性可选 | Partial<User> |
| Required<T> | 所有属性必选 | Required<Props> |
| Pick<T,K> | 选取部分属性 | Pick<User,’id’|’name’> |
| Omit<T,K> | 排除部分属性 | Omit<User,’password’> |
| Record<K,V> | 键值对类型 | Record<string,number> |
| ReturnType<T> | 函数返回值 | ReturnType<typeof fn> |
| Awaited<T> | 解包Promise | Awaited<Promise<User>> |
类型体操实战
类型安全的事件总线
type EventMap = {
click: { x: number; y: number };
hover: { element: string };
submit: { data: FormData };
};
class TypedEventBus<T extends Record> {
private handlers = new Map<keyof T, Set void>>();
on(event: K, handler: (payload: T[K]) => void) {
if (!this.handlers.has(event)) {
this.handlers.set(event, new Set());
}
this.handlers.get(event)!.add(handler);
}
emit(event: K, payload: T[K]) {
this.handlers.get(event)?.forEach(h => h(payload));
}
}
const bus = new TypedEventBus();
bus.on('click', ({ x, y }) => console.log(x, y)); // 类型安全
bus.emit('click', { x: 10, y: 20 }); // OK
bus.emit('click', { x: 10 }); // Error: 缺少y
深度只读
type DeepReadonly = {
readonly [K in keyof T]: T[K] extends object ? DeepReadonly : T[K];
};
interface Config {
server: { host: string; port: number };
debug: boolean;
}
const config: DeepReadonly = {
server: { host: 'localhost', port: 3000 },
debug: false,
};
config.server.host = 'other'; // Error: 只读
工程化实践
- tsconfig严格模式——开启strict: true,包括noImplicitAny、strictNullChecks等
- 类型导出规范——公共类型统一放在types目录,用export导出
- 避免any——用unknown代替any,使用时进行类型收窄
- 类型测试——用tsd或expect-type编写类型测试,确保类型变更不破坏API
- 自动生成类型——后端API用OpenAPI/Swagger生成TS类型,前后端共享
TypeScript高级类型不是为了炫技,而是为了让代码更安全、更易维护。从实际需求出发,逐步引入高级类型技巧,才能发挥TypeScript的最大价值。