TypeScript 高级类型体操:实际应用场景
写 TypeScript 三年,从 any 到高级类型,记录实际应用场景。
实用工具类型
DeepPartial
type DeepPartial<T> = T extends object
? { [P in keyof T]?: DeepPartial<T[P]> }
: T;
interface User {
name: string;
profile: {
age: number;
address: string;
};
}
const update: DeepPartial<User> = {
profile: { age: 25 }, // 不需要提供所有字段
};
DeepRequired
type DeepRequired<T> = T extends object
? { [P in keyof T]-?: DeepRequired<T[P]> }
: T;
DeepReadonly
type DeepReadonly<T> = T extends object
? { readonly [P in keyof T]: DeepReadonly<T[P]> }
: T;
const config: DeepReadonly<Config> = { ... };
config.api.url = 'new-url'; // 报错!
RequireKeys
type RequireKeys<T, K extends keyof T> = T & Required<Pick<T, K>>;
interface User {
id?: number;
name?: string;
email?: string;
}
type UserWithEmail = RequireKeys<User, 'email'>;
// email 必填,其他可选
条件类型实战
API 响应类型
type ApiResponse<T> = T extends { list: infer L }
? { data: L[]; total: number }
: { data: T };
// 使用
interface UserListResponse {
list: User;
total: number;
}
type Response = ApiResponse<UserListResponse>;
// { data: User[]; total: number }
type SingleResponse = ApiResponse<User>;
// { data: User }
函数参数提取
type ExtractParameters<T> = T extends (...args: infer P) => any ? P : never;
function createUser(name: string, age: number, email: string) {
return { name, age, email };
}
type CreateUserParams = ExtractParameters<typeof createUser>;
// [string, number, string]
Promise 值提取
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;
type Result = Awaited<Promise<Promise<string>>>;
// string
模板字面量类型
路由类型
type Route = '/users' | '/users/:id' | '/posts' | '/posts/:postId/comments/:commentId';
type ExtractParams<T extends string> = T extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: T extends `${string}:${infer Param}`
? Param
: never;
type Params = ExtractParams<Route>;
// 'id' | 'postId' | 'commentId'
事件名称生成
type Events = {
user: ['created', 'updated', 'deleted'];
post: ['created', 'published'];
};
type EventName<T extends keyof Events> = `${T}:${Events[T][number]}`;
type UserEvent = EventName<'user'>;
// 'user:created' | 'user:updated' | 'user:deleted'
映射类型实战
表单字段生成
interface Entity {
id: number;
name: string;
email: string;
}
type FormField<T> = {
[K in keyof T]: {
value: T[K];
error?: string;
touched: boolean;
};
};
type EntityForm = FormField<Entity>;
// {
// id: { value: number; error?: string; touched: boolean };
// name: { value: string; error?: string; touched: boolean };
// email: { value: string; error?: string; touched: boolean };
// }
API 参数类型
type QueryParams<T> = {
[K in keyof T]?: T[K] extends string
? T[K]
: T[K] extends number
? number | string
: T[K];
};
interface Filter {
name: string;
age: number;
status: 'active' | 'inactive';
}
type Params = QueryParams<Filter>;
// {
// name?: string;
// age?: number | string; // 支持 "18"
// status?: 'active' | 'inactive';
// }
类型守卫
自定义类型守卫
function isUser(value: unknown): value is User {
return (
typeof value === 'object' &&
value !== null &&
'id' in value &&
'name' in value &&
'email' in value
);
}
// 使用
if (isUser(data)) {
console.log(data.name); // 类型正确
}
断言函数
function assert(condition: unknown, message: string): asserts condition {
if (!condition) {
throw new Error(message);
}
}
function process(value: unknown) {
assert(typeof value === 'string', 'Value must be a string');
console.log(value.toUpperCase()); // 类型正确
}
类型体操 vs 实用性
| 场景 | 推荐程度 |
|---|---|
| 公共库类型定义 | 高 |
| 项目基础类型 | 高 |
| 业务逻辑类型 | 中 |
| 一次性脚本 | 低 |
不要过度:
// 过度:太复杂,维护困难
type DeepNestedCondition<T> = T extends object
? T extends Array<infer U>
? DeepNestedCondition<U>[]
: { [K in keyof T]: DeepNestedCondition<T[K]> }
: T;
// 适度:简单实用
type SafeDeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? SafeDeepPartial<T[P]> : T[P];
};
调试类型
类型显示
type ShowType<T> = { [K in keyof T]: T[K] };
// 鼠标悬停查看类型
type Result = ShowType<ComplexType>;
类型断言调试
type Debug<T> = T extends infer U ? U : never;
// 强制展开类型
type Expanded = Debug<SomeComplexGenericType>;
总结
类型体操是工具,不是目的。
使用原则:
- 解决实际问题
- 代码可读性优先
- 必要时加注释
- 复杂类型单独文件
好的类型设计让代码更安全、IDE 提示更友好。