还在重复拷贝TS类型?来感受下Utility Types的威力吧

208 阅读3分钟

COVER.png

一、工具类型核心概念

TypeScript 内置工具类型(Utility Types)是预定义的泛型类型,通过类型映射条件类型实现类型转换。它们能减少30%的类型重复定义,在大型项目中可提升代码可维护性。核心价值在于将静态类型检查从被动防御升级为主动推导。

二、基础工具类型详解

1. Partial<T>:可选化属性

原理:映射类型遍历属性添加?修饰符
基础示例

type User = { name: string; age: number };
type PartialUser = Partial<User>; // { name?: string; age?: number }

实战场景:嵌套对象更新

// 用户配置更新(支持嵌套对象)
type ServerConfig = {
  api: { endpoint: string; timeout: number };
  db: { host: string; poolSize: number };
};

function updateConfig(config: Partial<ServerConfig>) {
  // 允许部分更新嵌套属性
  return { ...defaultConfig, ...config };
}

// 仅更新数据库连接池大小
updateConfig({ db: { poolSize: 20 } });

2. Required<T>:必填化属性

原理:使用-?移除可选修饰符
基础示例

type OptionalUser = { name?: string };
type RequiredUser = Required<OptionalUser>; // { name: string }

实战场景:用户注册校验

// 强制要求所有必填字段
type RegistrationForm = {
  username?: string;
  password?: string;
  email?: string;
};

function validateForm(form: Required<RegistrationForm>) {
  // 确保所有字段已填写
  return Object.values(form).every(Boolean);
}

// 编译时报错:缺少password字段
validateForm({ username: "test", email: "test@example.com" });

3. Readonly<T>:只读化属性

原理:添加readonly修饰符
基础示例

type Config = Readonly<{ apiUrl: string }>;

实战场景:全局配置锁定

// 初始化后禁止修改
const envConfig: Readonly<{
  apiBase: string;
  maxRetries: number;
}> = Object.freeze({
  apiBase: "https://api.example.com",
  maxRetries: 3
});

// 编译时报错:无法分配到只读属性
envConfig.maxRetries = 5;

4. Pick<T, K> & Omit<T, K>:属性筛选

对比

  • Pick:白名单模式,选择指定属性
  • Omit:黑名单模式,排除指定属性

基础示例

// Pick示例补充
type User = { id: number; name: string; email: string };
type PublicProfile = Pick<User, 'id' | 'name'>; // { id: number; name: string }

实战场景:API响应过滤

// 敏感信息过滤
type FullUser = {
  id: number;
  name: string;
  passwordHash: string;
  creditCard?: string;
};

type SafeUser = Omit<FullUser, 'passwordHash' | 'creditCard'>;

function sanitizeUser(user: FullUser): SafeUser {
  const { passwordHash, creditCard, ...safeData } = user;
  return safeData; // { id: number; name: string }
}

三、高级工具类型解析

1. Record<K, T>:结构化对象

原理:映射键集合到统一值类型
实战场景:权限管理系统

// 角色权限矩阵
type Role = 'admin' | 'editor' | 'viewer';
type Permission = 'read' | 'write' | 'delete';

const permissions: Record<Role, Permission[]> = {
  admin: ['read', 'write', 'delete'],
  editor: ['read', 'write'],
  viewer: ['read']
};

// 动态权限检查
function checkPermission(role: Role, action: Permission): boolean {
  return permissions[role].includes(action);
}

2. Exclude<T, U> & Extract<T, U>:集合运算

数学建模

  • Exclude:差集运算 TUT - U
  • Extract:交集运算 TUT \cap U

实战场景:路由守卫

type AllRoutes = '/home' | '/profile' | '/admin' | '/login';
type ProtectedRoutes = Exclude<AllRoutes, '/login'>; // '/home' | '/profile' | '/admin'
    type AuthRoutes = Extract<AllRoutes, '/login' | '/register'>; // '/login' | '/register'

function guardRoute(route: ProtectedRoutes) {
  // 需要登录验证的逻辑
}

3. ReturnType<T> & Parameters<T>:函数类型提取

实战场景:API客户端封装

// 自动推导API类型
declare function fetchUser(id: string): Promise<{ name: string }>;

type ApiResponse<T> = Awaited<ReturnType<T>>;
type ApiParams<T> = Parameters<T>[0];

async function callApi<T extends (...args: any) => any>(
  func: T,
  params: ApiParams<T>
): Promise<ApiResponse<T>> {
  try {
    return await func(params);
  } catch (error) {
    // 统一错误处理
    throw new Error(`API调用失败: ${error}`);
  }
}

// 类型安全调用
const user = await callApi(fetchUser, 'user123'); // 自动推断返回类型为{ name: string }

四、实战应用场景

场景1:类型安全的API层

// 自动生成API类型
type ApiSchema = {
  '/users': {
    GET: { query: { page: number } };
    POST: { body: { name: string } };
  };
  '/products': {
    GET: { query: { category: string } };
  };
};

type ApiMethod<T extends keyof ApiSchema> = keyof ApiSchema[T];
type RequestParams<T extends keyof ApiSchema, M extends ApiMethod<T>> = {
  url: T;
  method: M;
} & ApiSchema[T][M];

function request<T extends keyof ApiSchema, M extends ApiMethod<T>>(
  params: RequestParams<T, M>
) {
  // 实现请求逻辑
}

// 编译时校验参数
request({
  url: '/users',
  method: 'POST',
  body: { name: 'Alice' } // 自动匹配类型
});

作用说明:通过类型推导实现端到端类型安全,避免手动定义DTO类型,当API Schema变更时自动检测类型错误

场景2:Redux状态管理

type ImmutableState = Readonly<{
  user: { id: string; name: string };
  cart: Record<string, { quantity: number }>;
}>;

function reducer(
  state: ImmutableState,
  action: { type: 'UPDATE_USER'; payload: Partial<ImmutableState['user']> }
) {
  return {
    ...state,
    user: { ...state.user, ...action.payload } // 确保不可变更新
  };
}

作用说明:通过ReadonlyPartial组合使用,在保持不可变性的同时允许局部更新,防止意外修改状态

五、最佳实践与性能优化

  1. 类型组合:优先使用type A = B & Omit<C, 'id'>而非多层嵌套
  2. 编译优化:复杂工具类型应控制在3层以内(TS 4.7+提升递归类型性能)
  3. 类型测试:使用// @ts-expect-error验证类型约束
// 测试Readonly约束
const config: Readonly<{ port: number }> = { port: 3000 };
// @ts-expect-error
config.port = 4000; // 确保触发编译错误

(完。感谢阅读,希望文章的内容对你有所帮助!)