前言
TypeScript 不仅仅是 JavaScript 的类型注解系统,它还提供了一套强大的内置工具类型(Utility Types),这些工具类型可以帮助我们更灵活地操作和转换现有的类型。对于初学者来说,掌握这些工具类型是进阶 TypeScript 开发的必经之路。
本文将详细介绍 TypeScript 中最常用的内置工具类型,并提供大量实用的代码示例。
什么是工具类型?
工具类型是 TypeScript 内置的泛型类型,用于从现有类型中派生出新类型。它们就像是类型系统中的"函数",接收一个或多个类型作为参数,返回一个新的类型。
typescript
// 基础接口
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 使用工具类型创建新类型
type PartialUser = Partial<User>; // 所有属性变为可选
type UserEmail = Pick<User, 'email'>; // 只保留 email 属性
type UserWithoutId = Omit<User, 'id'>; // 排除 id 属性
基础工具类型
1. Partial
作用:将类型 T 的所有属性变为可选属性。
typescript
interface User {
id: number;
name: string;
email: string;
age: number;
}
// 原始类型:所有属性都必需
const user1: User = {
id: 1,
name: "张三",
email: "zhangsan@example.com",
age: 25
}; // ✅ 正确
// 使用 Partial:所有属性都变为可选
type PartialUser = Partial<User>;
// 等价于:
// {
// id?: number;
// name?: string;
// email?: string;
// age?: number;
// }
const user2: PartialUser = {
name: "李四"
}; // ✅ 正确,只需要部分属性
const user3: PartialUser = {}; // ✅ 也正确,所有属性都是可选的
实际应用场景:
typescript
// 用户信息更新函数
function updateUser(id: number, updates: Partial<User>): User {
const existingUser = getUserById(id);
return { ...existingUser, ...updates };
}
// 使用时只需要传入要更新的字段
updateUser(1, { name: "新名字" });
updateUser(2, { age: 26, email: "new@example.com" });
2. Required
作用:将类型 T 的所有属性变为必需属性(与 Partial 相反)。
typescript
interface Config {
apiUrl?: string;
timeout?: number;
retries?: number;
debug?: boolean;
}
// 所有属性都是可选的
const config1: Config = {}; // ✅ 正确
// 使用 Required:所有属性都变为必需
type RequiredConfig = Required<Config>;
// 等价于:
// {
// apiUrl: string;
// timeout: number;
// retries: number;
// debug: boolean;
// }
const config2: RequiredConfig = {
apiUrl: "https://api.example.com",
timeout: 5000,
retries: 3,
debug: false
}; // ✅ 必须提供所有属性
// const config3: RequiredConfig = {}; // ❌ 错误,缺少必需属性
实际应用场景:
typescript
// 配置验证函数
function validateConfig(config: Config): Required<Config> {
return {
apiUrl: config.apiUrl || "https://default-api.com",
timeout: config.timeout || 3000,
retries: config.retries || 1,
debug: config.debug || false
};
}
3. Readonly
作用:将类型 T 的所有属性变为只读属性。
typescript
interface User {
id: number;
name: string;
email: string;
}
type ReadonlyUser = Readonly<User>;
// 等价于:
// {
// readonly id: number;
// readonly name: string;
// readonly email: string;
// }
const user: ReadonlyUser = {
id: 1,
name: "张三",
email: "zhangsan@example.com"
};
// user.name = "李四"; // ❌ 错误,只读属性不能修改
// user.id = 2; // ❌ 错误,只读属性不能修改
实际应用场景:
typescript
// 不可变的状态管理
interface AppState {
user: User | null;
isLoading: boolean;
error: string | null;
}
// 确保状态不被意外修改
function createState(initialState: AppState): Readonly<AppState> {
return Object.freeze(initialState);
}
// 只能通过专门的函数更新状态
function updateState(state: Readonly<AppState>, updates: Partial<AppState>): Readonly<AppState> {
return createState({ ...state, ...updates });
}
选择和排除工具类型
4. Pick<T, K>
作用:从类型 T 中选择指定的属性 K 创建新类型。
typescript
interface User {
id: number;
name: string;
email: string;
age: number;
phone: string;
address: string;
}
// 只选择 id 和 name
type UserSummary = Pick<User, 'id' | 'name'>;
// 等价于:
// {
// id: number;
// name: string;
// }
// 选择联系信息
type UserContact = Pick<User, 'email' | 'phone'>;
// 等价于:
// {
// email: string;
// phone: string;
// }
const summary: UserSummary = {
id: 1,
name: "张三"
}; // ✅ 只需要这两个属性
const contact: UserContact = {
email: "zhangsan@example.com",
phone: "13800138000"
}; // ✅ 只需要联系信息
实际应用场景:
typescript
// API 响应类型
interface ApiUser {
id: number;
name: string;
email: string;
age: number;
createdAt: string;
updatedAt: string;
password: string; // 敏感信息
}
// 公开的用户信息(排除敏感字段)
type PublicUser = Pick<ApiUser, 'id' | 'name' | 'email' | 'age'>;
// 登录表单类型
type LoginForm = Pick<ApiUser, 'email' | 'password'>;
// 注册表单类型
type RegisterForm = Pick<ApiUser, 'name' | 'email' | 'password' | 'age'>;
5. Omit<T, K>
作用:从类型 T 中排除指定的属性 K 创建新类型。
typescript
interface User {
id: number;
name: string;
email: string;
password: string;
createdAt: string;
updatedAt: string;
}
// 排除敏感信息
type SafeUser = Omit<User, 'password'>;
// 等价于:
// {
// id: number;
// name: string;
// email: string;
// createdAt: string;
// updatedAt: string;
// }
// 排除系统字段
type UserInput = Omit<User, 'id' | 'createdAt' | 'updatedAt'>;
// 等价于:
// {
// name: string;
// email: string;
// password: string;
// }
const safeUser: SafeUser = {
id: 1,
name: "张三",
email: "zhangsan@example.com",
createdAt: "2024-01-01",
updatedAt: "2024-01-02"
}; // ✅ 不包含密码
const userInput: UserInput = {
name: "新用户",
email: "newuser@example.com",
password: "123456"
}; // ✅ 不包含系统生成的字段
Pick vs Omit 的选择:
typescript
interface LargeInterface {
prop1: string;
prop2: string;
prop3: string;
prop4: string;
prop5: string;
// ... 还有很多属性
}
// 如果只需要少数几个属性,用 Pick
type Small = Pick<LargeInterface, 'prop1' | 'prop2'>;
// 如果只排除少数几个属性,用 Omit
type AlmostAll = Omit<LargeInterface, 'prop5'>;
键值对工具类型
6. Record<K, T>
作用:创建一个键类型为 K、值类型为 T 的对象类型。
typescript
// 基础用法
type StringRecord = Record<string, string>;
// 等价于:{ [key: string]: string }
type NumberRecord = Record<string, number>;
// 等价于:{ [key: string]: number }
// 使用联合类型作为键
type Theme = 'light' | 'dark';
type ThemeColors = Record<Theme, string>;
// 等价于:
// {
// light: string;
// dark: string;
// }
const colors: ThemeColors = {
light: "#ffffff",
dark: "#000000"
}; // ✅ 必须包含所有主题
// 更复杂的例子
type Status = 'loading' | 'success' | 'error';
type StatusConfig = Record<Status, {
message: string;
color: string;
icon: string;
}>;
const statusConfig: StatusConfig = {
loading: {
message: "正在加载...",
color: "blue",
icon: "spinner"
},
success: {
message: "操作成功",
color: "green",
icon: "check"
},
error: {
message: "操作失败",
color: "red",
icon: "error"
}
};
实际应用场景:
typescript
// 表单验证规则
type FormFields = 'username' | 'email' | 'password';
type ValidationRules = Record<FormFields, {
required: boolean;
minLength?: number;
pattern?: RegExp;
}>;
const formValidation: ValidationRules = {
username: {
required: true,
minLength: 3
},
email: {
required: true,
pattern: /^[^\s@]+@[^\s@]+\.[^\s@]+$/
},
password: {
required: true,
minLength: 8
}
};
// 国际化文本
type Language = 'zh-CN' | 'en-US';
type Translations = Record<Language, Record<string, string>>;
const translations: Translations = {
'zh-CN': {
welcome: "欢迎",
goodbye: "再见"
},
'en-US': {
welcome: "Welcome",
goodbye: "Goodbye"
}
};
联合类型工具类型
7. Exclude<T, U>
作用:从联合类型 T 中排除可以赋值给 U 的类型。
typescript
type AllColors = 'red' | 'green' | 'blue' | 'yellow' | 'purple';
type PrimaryColors = 'red' | 'green' | 'blue';
// 排除主要颜色,得到次要颜色
type SecondaryColors = Exclude<AllColors, PrimaryColors>;
// 结果:'yellow' | 'purple'
// 更实际的例子
type AllEvents = 'click' | 'scroll' | 'resize' | 'load' | 'error';
type MouseEvents = 'click';
type WindowEvents = Exclude<AllEvents, MouseEvents>;
// 结果:'scroll' | 'resize' | 'load' | 'error'
// 排除特定类型
type StringOrNumber = string | number | boolean;
type OnlyStringOrNumber = Exclude<StringOrNumber, boolean>;
// 结果:string | number
实际应用场景:
typescript
// HTTP 方法
type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
type ReadOnlyMethods = 'GET';
type MutatingMethods = Exclude<HttpMethod, ReadOnlyMethods>;
// 结果:'POST' | 'PUT' | 'DELETE' | 'PATCH'
// 权限系统
type AllPermissions = 'read' | 'write' | 'delete' | 'admin';
type UserPermissions = 'read' | 'write';
type AdminOnlyPermissions = Exclude<AllPermissions, UserPermissions>;
// 结果:'delete' | 'admin'
function checkPermission(permission: AdminOnlyPermissions) {
// 只接受管理员专有权限
console.log(`检查管理员权限: ${permission}`);
}
8. Extract<T, U>
作用:从联合类型 T 中提取可以赋值给 U 的类型(与 Exclude 相反)。
typescript
type AllColors = 'red' | 'green' | 'blue' | 'yellow' | 'purple';
type WarmColors = 'red' | 'yellow' | 'orange';
// 提取暖色调
type WarmColorsAvailable = Extract<AllColors, WarmColors>;
// 结果:'red' | 'yellow'
// 字符串和数字的联合类型
type StringOrNumber = string | number | boolean;
type OnlyStrings = Extract<StringOrNumber, string>;
// 结果:string
// 更复杂的例子
type ApiResponse =
| { type: 'success'; data: any }
| { type: 'error'; message: string }
| { type: 'loading' }
| { type: 'idle' };
type ResponseWithData = Extract<ApiResponse, { data: any }>;
// 结果:{ type: 'success'; data: any }
实际应用场景:
typescript
// 事件处理
type DOMEvents = 'click' | 'scroll' | 'keydown' | 'resize' | 'load';
type KeyboardEvents = 'keydown' | 'keyup' | 'keypress';
type SupportedKeyboardEvents = Extract<DOMEvents, KeyboardEvents>;
// 结果:'keydown'
// 数据类型过滤
type DataTypes = string | number | boolean | object | null | undefined;
type PrimitiveTypes = string | number | boolean;
type SupportedPrimitives = Extract<DataTypes, PrimitiveTypes>;
// 结果:string | number | boolean
function processPrimitive(value: SupportedPrimitives) {
// 只处理原始类型
console.log(`处理原始值: ${value}`);
}
9. NonNullable
作用:从类型 T 中排除 null 和 undefined。
typescript
type MaybeString = string | null | undefined;
type DefinitelyString = NonNullable<MaybeString>;
// 结果:string
type MaybeUser = User | null | undefined;
type DefinitelyUser = NonNullable<MaybeUser>;
// 结果:User
// 实际应用
function processUser(user: User | null | undefined) {
if (user) {
// 在这个分支中,user 的类型自动变为 NonNullable<typeof user>
const safeUser: NonNullable<typeof user> = user;
console.log(safeUser.name); // ✅ 安全访问
}
}
// 数组过滤
const users: (User | null | undefined)[] = [
{ id: 1, name: "张三", email: "zhangsan@example.com", age: 25 },
null,
{ id: 2, name: "李四", email: "lisi@example.com", age: 30 },
undefined
];
const validUsers: NonNullable<(User | null | undefined)>[] = users.filter(
(user): user is NonNullable<typeof user> => user != null
);
// validUsers 的类型是 User[]
函数相关工具类型
10. Parameters
作用:获取函数类型 T 的参数类型组成的元组。
typescript
// 基础函数
function greet(name: string, age: number): string {
return `Hello, ${name}! You are ${age} years old.`;
}
type GreetParams = Parameters<typeof greet>;
// 结果:[string, number]
// 使用参数类型
function callGreet(...args: GreetParams) {
return greet(...args);
}
// 更复杂的例子
type AsyncFunction = (id: number, options?: { timeout: number }) => Promise<User>;
type AsyncParams = Parameters<AsyncFunction>;
// 结果:[number, ({ timeout: number } | undefined)?]
// 实际应用:高阶函数
function withLogging<T extends (...args: any[]) => any>(
fn: T,
logMessage: string
) {
return (...args: Parameters<T>) => {
console.log(logMessage, args);
return fn(...args);
};
}
const loggedGreet = withLogging(greet, "调用 greet 函数:");
loggedGreet("张三", 25); // 自动推断参数类型
11. ReturnType
作用:获取函数类型 T 的返回值类型。
typescript
function getUser(): { id: number; name: string } {
return { id: 1, name: "张三" };
}
type UserReturnType = ReturnType<typeof getUser>;
// 结果:{ id: number; name: string }
// 异步函数
async function fetchUser(): Promise<User> {
return await api.getUser();
}
type FetchUserReturnType = ReturnType<typeof fetchUser>;
// 结果:Promise<User>
// 实际应用:类型守卫
function isString(value: unknown): value is string {
return typeof value === 'string';
}
type IsStringReturnType = ReturnType<typeof isString>;
// 结果:value is string
// 工厂函数
function createApiClient() {
return {
get: (url: string) => fetch(url),
post: (url: string, data: any) => fetch(url, { method: 'POST', body: JSON.stringify(data) })
};
}
type ApiClient = ReturnType<typeof createApiClient>;
// 结果:{
// get: (url: string) => Promise<Response>;
// post: (url: string, data: any) => Promise<Response>;
// }
12. ConstructorParameters
作用:获取构造函数类型 T 的参数类型组成的元组。
typescript
class User {
constructor(name: string, email: string, age?: number) {
// 构造函数实现
}
}
type UserConstructorParams = ConstructorParameters<typeof User>;
// 结果:[string, string, (number | undefined)?]
// 使用构造函数参数类型
function createUser(...args: UserConstructorParams): User {
return new User(...args);
}
// 内置类型的构造函数参数
type DateParams = ConstructorParameters<typeof Date>;
// 结果:[] | [string | number | Date] | [number, number, number?, number?, number?, number?, number?]
type ArrayParams = ConstructorParameters<typeof Array>;
// 结果:any[]
// 实际应用:工厂模式
class ApiClient {
constructor(
private baseUrl: string,
private apiKey: string,
private timeout: number = 5000
) {}
}
type ApiClientParams = ConstructorParameters<typeof ApiClient>;
function createApiClient(...args: ApiClientParams): ApiClient {
console.log('创建 API 客户端,参数:', args);
return new ApiClient(...args);
}
13. InstanceType
作用:获取构造函数类型 T 的实例类型。
typescript
class User {
constructor(public name: string, public email: string) {}
greet() {
return `Hello, I'm ${this.name}`;
}
}
type UserInstance = InstanceType<typeof User>;
// 结果:User
// 使用实例类型
function processUser(user: UserInstance) {
console.log(user.greet());
console.log(user.name);
console.log(user.email);
}
// 泛型构造函数
interface Constructable<T = {}> {
new (...args: any[]): T;
}
function createInstance<T extends Constructable>(
ctor: T,
...args: ConstructorParameters<T>
): InstanceType<T> {
return new ctor(...args);
}
const user = createInstance(User, "张三", "zhangsan@example.com");
// user 的类型自动推断为 User
字符串操作工具类型
14. Uppercase、Lowercase、Capitalize、Uncapitalize
这些是 TypeScript 4.1+ 新增的字符串字面量类型操作工具。
typescript
// Uppercase - 转为大写
type UpperHello = Uppercase<'hello world'>;
// 结果:'HELLO WORLD'
// Lowercase - 转为小写
type LowerHello = Lowercase<'HELLO WORLD'>;
// 结果:'hello world'
// Capitalize - 首字母大写
type CapitalHello = Capitalize<'hello world'>;
// 结果:'Hello world'
// Uncapitalize - 首字母小写
type UncapitalHello = Uncapitalize<'Hello World'>;
// 结果:'hello World'
// 实际应用:HTTP 方法
type HttpMethod = 'get' | 'post' | 'put' | 'delete';
type HttpMethodUpper = Uppercase<HttpMethod>;
// 结果:'GET' | 'POST' | 'PUT' | 'DELETE'
// CSS 属性转换
type CSSProperty = 'background-color' | 'font-size' | 'margin-top';
type CamelCaseProperty = Capitalize<CSSProperty>;
// 注意:这只是首字母大写,不会转换连字符
// 实际的驼峰命名转换需要更复杂的类型操作
type KebabToCamel<S extends string> = S extends `${infer P1}-${infer P2}${infer P3}`
? `${P1}${Capitalize<KebabToCamel<`${P2}${P3}`>>}`
: S;
type CamelCSS = KebabToCamel<'background-color'>;
// 结果:'backgroundColor'
高级组合应用
综合示例:用户管理系统
typescript
// 基础用户接口
interface BaseUser {
id: number;
username: string;
email: string;
password: string;
firstName: string;
lastName: string;
age: number;
role: 'admin' | 'user' | 'moderator';
createdAt: string;
updatedAt: string;
lastLoginAt: string | null;
}
// 1. 公开用户信息(排除敏感字段)
type PublicUser = Omit<BaseUser, 'password' | 'email'>;
// 2. 用户注册表单(排除系统字段)
type UserRegistration = Omit<BaseUser, 'id' | 'createdAt' | 'updatedAt' | 'lastLoginAt'>;
// 3. 用户更新表单(部分字段可选,排除系统字段)
type UserUpdate = Partial<Omit<BaseUser, 'id' | 'createdAt' | 'updatedAt'>>;
// 4. 用户登录表单
type UserLogin = Pick<BaseUser, 'username' | 'password'>;
// 5. 只读用户信息
type ReadonlyUser = Readonly<PublicUser>;
// 6. 用户角色映射
type RolePermissions = Record<BaseUser['role'], {
canRead: boolean;
canWrite: boolean;
canDelete: boolean;
canManageUsers: boolean;
}>;
const rolePermissions: RolePermissions = {
admin: {
canRead: true,
canWrite: true,
canDelete: true,
canManageUsers: true
},
moderator: {
canRead: true,
canWrite: true,
canDelete: false,
canManageUsers: false
},
user: {
canRead: true,
canWrite: false,
canDelete: false,
canManageUsers: false
}
};
// 7. API 响应类型
type ApiResponse<T> = {
success: boolean;
data: T;
message?: string;
errors?: Record<string, string[]>;
};
// 8. 用户服务类型
class UserService {
async register(userData: UserRegistration): Promise<ApiResponse<PublicUser>> {
// 实现注册逻辑
throw new Error("未实现");
}
async login(credentials: UserLogin): Promise<ApiResponse<{ user: PublicUser; token: string }>> {
// 实现登录逻辑
throw new Error("未实现");
}
async updateUser(id: number, updates: UserUpdate): Promise<ApiResponse<PublicUser>> {
// 实现更新逻辑
throw new Error("未实现");
}
async getUser(id: number): Promise<ApiResponse<PublicUser>> {
// 实现获取用户逻辑
throw new Error("未实现");
}
}
// 9. 类型守卫函数
function isValidRole(role: string): role is BaseUser['role'] {
return ['admin', 'user', 'moderator'].includes(role);
}
// 10. 工厂函数
function createUserService(): UserService {
return new UserService();
}
type UserServiceType = ReturnType<typeof createUserService>;
表单验证系统
typescript
// 表单字段类型
interface FormField {
value: string;
error?: string;
touched: boolean;
required: boolean;
}
// 表单配置
interface FormConfig {
username: FormField;
email: FormField;
password: FormField;
confirmPassword: FormField;
}
// 1. 只获取表单值
type FormValues = {
[K in keyof FormConfig]: FormConfig[K]['value'];
};
// 结果:{
// username: string;
// email: string;
// password: string;
// confirmPassword: string;
// }
// 2. 只获取表单错误
type FormErrors = {
[K in keyof FormConfig]: FormConfig[K]['error'];
};
// 3. 验证规则类型
type ValidationRule<T> = (value: T) => string | undefined;
type FormValidation = {
[K in keyof FormValues]: ValidationRule<FormValues[K]>[];
};
// 4. 表单状态
type FormState = FormConfig & {
isValid: boolean;
isSubmitting: boolean;
};
// 5. 表单操作
type FormActions = {
updateField: <K extends keyof FormValues>(field: K, value: FormValues[K]) => void;
validateField: <K extends keyof FormValues>(field: K) => void;
validateForm: () => boolean;
resetForm: () => void;
submitForm: () => Promise<void>;
};
// 使用示例
const formValidation: FormValidation = {
username: [
(value) => value.length < 3 ? '用户名至少3个字符' : undefined,
(value) => /^[a-zA-Z0-9_]+$/.test(value) ? undefined : '用户名只能包含字母、数字和下划线'
],
email: [
(value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value) ? undefined : '请输入有效的邮箱地址'
],
password: [
(value) => value.length < 8 ? '密码至少8个字符' : undefined
],
confirmPassword: [
(value) => value === '' ? '请确认密码' : undefined
]
};
性能和最佳实践
1. 避免过度复杂的类型操作
typescript
// ❌ 避免:过度复杂的类型操作
type OverlyComplex<T> = {
[K in keyof T as T[K] extends string
? Uppercase<K extends string ? K : never>
: K extends string
? Lowercase<K>
: K
]: T[K] extends string
? Capitalize<T[K]>
: T[K] extends number
? `${T[K]}`
: T[K]
};
// ✅ 推荐:简单明了的类型操作
type SimpleTransform<T> = {
[K in keyof T]: string;
};
2. 合理使用类型缓存
typescript
// ❌ 避免:重复计算复杂类型
function processUsers(users: Omit<User, 'password'>[]): Pick<User, 'id' | 'name'>[] {
return users.map(user => ({
id: user.id,
name: user.name
}));
}
function displayUsers(users: Omit<User, 'password'>[]): void {
// 重复定义相同的类型
}
// ✅ 推荐:缓存常用类型
type SafeUser = Omit<User, 'password'>;
type UserSummary = Pick<User, 'id' | 'name'>;
function processUsers(users: SafeUser[]): UserSummary[] {
return users.map(user => ({
id: user.id,
name: user.name
}));
}
function displayUsers(users: SafeUser[]): void {
// 复用已定义的类型
}
3. 渐进式类型增强
typescript
// 从宽松开始,逐步严格化
interface ApiResponseV1 {
data: any; // 初期使用 any
success: boolean;
}
interface ApiResponseV2<T = any> {
data: T; // 添加泛型支持
success: boolean;
message?: string;
}
interface ApiResponseV3<T = any> {
data: T;
success: boolean;
message?: string;
errors?: Record<string, string[]>; // 添加错误处理
metadata?: {
total?: number;
page?: number;
limit?: number;
};
}
// 当前推荐使用的版本
type ApiResponse<T = any> = ApiResponseV3<T>;
4. 条件类型的智能应用
typescript
// 根据条件选择不同的类型
type ApiConfig<T extends 'development' | 'production'> = T extends 'development'
? {
apiUrl: string;
debug: true;
mockData: boolean;
logLevel: 'verbose' | 'debug' | 'info';
}
: {
apiUrl: string;
debug: false;
logLevel: 'warn' | 'error';
};
// 使用
const devConfig: ApiConfig<'development'> = {
apiUrl: 'http://localhost:3000',
debug: true,
mockData: true,
logLevel: 'verbose'
};
const prodConfig: ApiConfig<'production'> = {
apiUrl: 'https://api.production.com',
debug: false,
logLevel: 'error'
};
常见错误和解决方案
1. 循环引用问题
typescript
// ❌ 错误:可能导致循环引用
interface Node {
value: string;
children: Node[];
parent: Node | null;
}
type NodePaths = {
[K in keyof Node]: Node[K] extends Node[]
? NodePaths[] // 这里可能产生循环引用
: Node[K];
};
// ✅ 解决方案:使用更简单的类型或避免深度递归
interface SimpleNode {
value: string;
children: SimpleNode[];
parent: SimpleNode | null;
}
type NodeValue = SimpleNode['value'];
type NodeChildren = SimpleNode['children'];
2. 过度使用联合类型
typescript
// ❌ 避免:过多的联合类型选项
type Status = 'loading' | 'success' | 'error' | 'idle' | 'pending' | 'cancelled' | 'timeout' | 'retrying';
// ✅ 推荐:分组管理
type LoadingState = 'idle' | 'loading' | 'pending';
type SuccessState = 'success';
type ErrorState = 'error' | 'timeout' | 'cancelled';
type RetryState = 'retrying';
type Status = LoadingState | SuccessState | ErrorState | RetryState;
// 或者使用更结构化的方式
type AsyncState =
| { status: 'idle' }
| { status: 'loading' }
| { status: 'success'; data: any }
| { status: 'error'; error: string };
3. 类型断言的误用
typescript
// ❌ 错误:过度使用类型断言
function processApiResponse(response: unknown) {
const data = response as { users: User[] }; // 不安全
return data.users.map(user => user.name);
}
// ✅ 推荐:使用类型守卫
function isUsersResponse(response: unknown): response is { users: User[] } {
return (
typeof response === 'object' &&
response !== null &&
'users' in response &&
Array.isArray((response as any).users)
);
}
function processApiResponse(response: unknown) {
if (isUsersResponse(response)) {
return response.users.map(user => user.name); // 类型安全
}
throw new Error('Invalid response format');
}
实际项目中的工具类型应用
React 组件类型
typescript
import React from 'react';
// 基础组件 Props
interface BaseButtonProps {
children: React.ReactNode;
onClick?: () => void;
disabled?: boolean;
variant?: 'primary' | 'secondary' | 'danger';
size?: 'small' | 'medium' | 'large';
}
// 使用工具类型创建变体
type IconButtonProps = Omit<BaseButtonProps, 'children'> & {
icon: React.ReactNode;
'aria-label': string;
};
type LinkButtonProps = Omit<BaseButtonProps, 'onClick'> & {
href: string;
target?: string;
};
type LoadingButtonProps = BaseButtonProps & {
loading?: boolean;
loadingText?: string;
};
// 组件实现
const Button: React.FC<BaseButtonProps> = ({ children, ...props }) => {
return <button {...props}>{children}</button>;
};
const IconButton: React.FC<IconButtonProps> = ({ icon, ...props }) => {
return <Button {...props}>{icon}</Button>;
};
const LinkButton: React.FC<LinkButtonProps> = ({ href, children, ...props }) => {
return <a href={href} {...props}>{children}</a>;
};
// 高阶组件类型
type WithLoadingProps<P> = P & {
loading?: boolean;
loadingComponent?: React.ReactNode;
};
function withLoading<P extends object>(
Component: React.ComponentType<P>
): React.ComponentType<WithLoadingProps<P>> {
return ({ loading, loadingComponent, ...props }) => {
if (loading) {
return <>{loadingComponent || 'Loading...'}</>;
}
return <Component {...(props as P)} />;
};
}
const LoadingButton = withLoading(Button);
API 客户端类型
typescript
// API 端点定义
interface ApiEndpoints {
users: {
list: { method: 'GET'; response: User[] };
create: { method: 'POST'; body: Omit<User, 'id'>; response: User };
update: { method: 'PUT'; body: Partial<User>; response: User };
delete: { method: 'DELETE'; response: void };
};
posts: {
list: { method: 'GET'; response: Post[] };
create: { method: 'POST'; body: Omit<Post, 'id'>; response: Post };
};
}
// 提取所有 API 方法
type ApiMethods = {
[Resource in keyof ApiEndpoints]: {
[Action in keyof ApiEndpoints[Resource]]: ApiEndpoints[Resource][Action] extends {
method: infer M;
response: infer R;
body?: infer B;
}
? {
method: M;
response: R;
body: B;
}
: never;
};
};
// 创建类型安全的 API 客户端
class ApiClient {
async request<
Resource extends keyof ApiEndpoints,
Action extends keyof ApiEndpoints[Resource]
>(
resource: Resource,
action: Action,
body?: ApiEndpoints[Resource][Action] extends { body: infer B } ? B : never
): Promise<ApiEndpoints[Resource][Action] extends { response: infer R } ? R : never> {
// 实现 API 请求逻辑
throw new Error('Not implemented');
}
}
// 使用示例
const api = new ApiClient();
// 类型安全的 API 调用
api.request('users', 'list'); // 返回 Promise<User[]>
api.request('users', 'create', { name: '张三', email: 'zhangsan@example.com' }); // 需要 body
api.request('users', 'delete'); // 返回 Promise<void>
状态管理类型
typescript
// Redux/状态管理相关类型
interface AppState {
user: {
currentUser: User | null;
isAuthenticated: boolean;
loading: boolean;
};
posts: {
items: Post[];
selectedPost: Post | null;
loading: boolean;
error: string | null;
};
ui: {
theme: 'light' | 'dark';
sidebarOpen: boolean;
notifications: Notification[];
};
}
// 提取特定模块的状态类型
type UserState = AppState['user'];
type PostsState = AppState['posts'];
type UIState = AppState['ui'];
// Action 类型
type Action<T extends string, P = void> = P extends void
? { type: T }
: { type: T; payload: P };
type UserActions =
| Action<'USER_LOGIN_START'>
| Action<'USER_LOGIN_SUCCESS', User>
| Action<'USER_LOGIN_FAILURE', string>
| Action<'USER_LOGOUT'>;
type PostActions =
| Action<'POSTS_LOAD_START'>
| Action<'POSTS_LOAD_SUCCESS', Post[]>
| Action<'POSTS_LOAD_FAILURE', string>
| Action<'POST_SELECT', Post>
| Action<'POST_CLEAR_SELECTION'>;
type UIActions =
| Action<'UI_TOGGLE_THEME'>
| Action<'UI_TOGGLE_SIDEBAR'>
| Action<'UI_ADD_NOTIFICATION', Notification>
| Action<'UI_REMOVE_NOTIFICATION', string>;
type AllActions = UserActions | PostActions | UIActions;
// Reducer 类型
type Reducer<S, A> = (state: S, action: A) => S;
const userReducer: Reducer<UserState, UserActions> = (state, action) => {
switch (action.type) {
case 'USER_LOGIN_SUCCESS':
return {
...state,
currentUser: action.payload, // TypeScript 知道这是 User 类型
isAuthenticated: true,
loading: false
};
// 其他 case...
default:
return state;
}
};
// Selector 类型
type Selector<S, R> = (state: S) => R;
const selectCurrentUser: Selector<AppState, User | null> = (state) => state.user.currentUser;
const selectIsAuthenticated: Selector<AppState, boolean> = (state) => state.user.isAuthenticated;
const selectPostsLoading: Selector<AppState, boolean> = (state) => state.posts.loading;
工具类型的性能考虑
1. 编译时性能
typescript
// ❌ 避免:过度复杂的递归类型
type DeepReadonly<T> = {
readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};
// 对于深度嵌套的对象,这可能导致编译性能问题
// ✅ 推荐:限制递归深度或使用更简单的方案
type SimpleReadonly<T> = Readonly<T>;
// 或者使用现有的库如 ts-toolbelt
// import { Object } from 'ts-toolbelt';
// type DeepReadonly<T> = Object.Readonly<T, 'deep'>;
2. 类型检查性能
typescript
// ❌ 避免:过多的条件类型判断
type ComplexConditional<T> = T extends string
? T extends `${infer P1}${infer P2}`
? P1 extends 'a'
? P2 extends 'b'
? 'matched-ab'
: P2 extends 'c'
? 'matched-ac'
: 'no-match'
: 'no-match'
: 'no-match'
: 'not-string';
// ✅ 推荐:使用更直接的方式
type SimpleString<T> = T extends string ? `processed-${T}` : never;
3. 开发体验优化
typescript
// 为复杂类型提供辅助函数
type DeepPartial<T> = {
[P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};
// 提供类型助手函数
function createPartialUpdate<T>(update: DeepPartial<T>): DeepPartial<T> {
return update;
}
// 使用时有更好的智能提示
const userUpdate = createPartialUpdate<User>({
name: "新名字",
// IDE 会提供完整的类型提示
});
总结与最佳实践
核心要点
- 从简单开始:先使用基础工具类型,逐步学习复杂的组合
- 类型复用:将常用的类型组合提取为独立的类型别名
- 渐进增强:从宽松的类型开始,逐步添加约束
- 性能考虑:避免过度复杂的类型操作
- 可读性优先:选择清晰易懂的类型定义而非过分巧妙的实现
学习路径建议
初级阶段
- 掌握
Partial
、Required
、Pick
、Omit
- 理解
Record
的基本用法 - 学会使用
Readonly
中级阶段
- 学习
Extract
、Exclude
、NonNullable
- 掌握函数相关工具类型:
Parameters
、ReturnType
- 理解条件类型的基础应用
高级阶段
- 掌握复杂的类型组合和转换
- 学习模板字面量类型
- 创建自定义工具类型
实际项目应用建议
- 建立类型库 :在项目中创建
types/
目录,统一管理常用类型 - 文档化:为复杂的类型定义添加注释说明
- 测试类型:使用类型测试确保类型定义的正确性
- 版本管理:为 API 类型提供版本控制支持
typescript
// types/utils.ts
export type SafeUser = Omit<User, 'password'>;
export type UserInput = Omit<User, 'id' | 'createdAt' | 'updatedAt'>;
export type ApiResponse<T> = {
success: boolean;
data: T;
message?: string;
};
// types/api.ts
export interface ApiEndpoints {
// API 端点定义
}
// types/components.ts
export type ComponentProps<T> = T & {
className?: string;
'data-testid'?: string;
};
错误处理模式
typescript
// 结果类型模式
type Result<T, E = Error> =
| { success: true; data: T }
| { success: false; error: E };
// 使用示例
async function fetchUser(id: number): Promise<Result<User, string>> {
try {
const user = await api.getUser(id);
return { success: true, data: user };
} catch (error) {
return { success: false, error: error.message };
}
}
// 类型安全的错误处理
const result = await fetchUser(1);
if (result.success) {
console.log(result.data.name); // TypeScript 知道这里 data 是 User 类型
} else {
console.error(result.error); // TypeScript 知道这里 error 是 string 类型
}