折腾侠
技术教程

TypeScript 高级类型体操:实用类型工具与模式完全指南

本文深入讲解 TypeScript 高级类型系统,涵盖内置实用类型工具(Partial、Required、Pick、Omit、Record)、条件类型与 infer 关键字、映射类型的属性转换技巧。通过构建类型安全的事件系统展示实战应用,并提供 DeepPartial、DeepReadonly 等实用工具类型实现。文章包含大量可运行代码示例,帮助开发者掌握类型体操,编写更安全、可维护的 TypeScript 代码。

折腾侠
2026/03/26 发布
12约 11 分钟1227 字 / 1634 词00

TypeScript 高级类型体操:实用类型工具与模式完全指南

前言

TypeScript 的类型系统远不止是简单的类型注解。掌握高级类型技巧,可以让你写出更安全、更可维护、更具表达力的代码。本文将深入探讨 TypeScript 的实用类型工具、条件类型、映射类型以及实际应用场景,帮助你真正驾驭类型系统。

一、内置实用类型工具详解

TypeScript 提供了一系列内置的实用类型工具,它们是日常开发中最常用的武器。

1.1 Partial - 让所有属性变为可选

TypeScript
interface User {
  id: number;
  name: string;
  email: string;
  age: number;
}

// 使用 Partial 让所有属性变为可选
type PartialUser = Partial<User>;
// 等价于:
// type PartialUser = {
//   id?: number;
//   name?: string;
//   email?: string;
//   age?: number;
// };

// 实际应用场景:更新操作
function updateUser(id: number, updates: Partial<User>): User {
  // 只需要传入需要更新的字段
  return { id, name: "John", email: "john@example.com", age: 25, ...updates };
}

updateUser(1, { name: "Jane" }); // 只更新 name
updateUser(2, { email: "new@example.com", age: 30 }); // 更新多个字段

1.2 Required - 让所有属性变为必填

TypeScript
interface Config {
  host?: string;
  port?: number;
  timeout?: number;
  retries?: number;
}

// 使用 Required 让所有属性变为必填
type RequiredConfig = Required<Config>;

// 实际应用场景:配置合并时的默认值保证
const defaultConfig: Required<Config> = {
  host: "localhost",
  port: 8080,
  timeout: 5000,
  retries: 3
};

function mergeConfig(userConfig: Config): RequiredConfig {
  return { ...defaultConfig, ...userConfig };
}

1.3 Pick<T, K> - 选择指定属性

TypeScript
interface Product {
  id: string;
  name: string;
  price: number;
  description: string;
  category: string;
  stock: number;
}

// 只选择 id、name 和 price
type ProductSummary = Pick<Product, "id" | "name" | "price">;

// 实际应用场景:API 响应数据精简
function getProductSummary(product: Product): ProductSummary {
  return {
    id: product.id,
    name: product.name,
    price: product.price
  };
}

// 列表展示时只需要基本信息
const products: ProductSummary[] = [
  { id: "1", name: "Laptop", price: 999 },
  { id: "2", name: "Mouse", price: 29 }
];

1.4 Omit<T, K> - 排除指定属性

TypeScript
interface User {
  id: string;
  name: string;
  email: string;
  password: string;
  role: string;
}

// 排除敏感信息
type PublicUser = Omit<User, "password" | "role">;

// 实际应用场景:API 响应中排除敏感字段
function sanitizeUser(user: User): PublicUser {
  const { password, role, ...publicData } = user;
  return publicData;
}

// 前端只接收公开信息
const publicUser: PublicUser = sanitizeUser({
  id: "1",
  name: "John",
  email: "john@example.com",
  password: "secret123",
  role: "admin"
});

1.5 Record<K, T> - 构建键值映射

TypeScript
// 创建枚举类型的映射
type Status = "pending" | "processing" | "completed" | "failed";

interface StatusConfig {
  label: string;
  color: string;
  icon: string;
}

// 使用 Record 构建状态配置映射
const statusConfig: Record<Status, StatusConfig> = {
  pending: { label: "待处理", color: "#ff9800", icon: "⏳" },
  processing: { label: "处理中", color: "#2196f3", icon: "🔄" },
  completed: { label: "已完成", color: "#4caf50", icon: "✅" },
  failed: { label: "失败", color: "#f44336", icon: "❌" }
};

// 实际应用场景:动态获取状态配置
function getStatusDisplay(status: Status): StatusConfig {
  return statusConfig[status];
}

二、条件类型:类型系统的三元运算符

条件类型是 TypeScript 类型系统的核心特性,语法为 INLINE_CODE_0

2.1 基础条件类型

TypeScript
// 判断类型是否为字符串
type IsString<T> = T extends string ? true : false;

type A = IsString<string>;  // true
type B = IsString<number>;  // false
type C = IsString<string | number>; // true | false

// 提取函数的返回类型
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function getUser(): { id: number; name: string } {
  return { id: 1, name: "John" };
}

type User = ReturnType<typeof getUser>; // { id: number; name: string }

2.2 infer 关键字:类型推断

TypeScript
// 提取数组元素类型
type ArrayElement<T> = T extends (infer U)[] ? U : never;

type A = ArrayElement<string[]>; // string
type B = ArrayElement<number[]>; // number
type C = ArrayElement<{ id: number }[]>; // { id: number }

// 提取 Promise 的内部类型
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;

type A = UnwrapPromise<Promise<string>>; // string
type B = UnwrapPromise<Promise<{ id: number }>>; // { id: number }
type C = UnwrapPromise<string>; // string (非 Promise 保持不变)

// 实际应用场景:API 响应类型提取
interface ApiResponse<T> {
  data: T;
  status: number;
  message: string;
}

type ExtractData<T> = T extends ApiResponse<infer U> ? U : never;

type UserData = ExtractData<ApiResponse<{ id: number; name: string }>>;
// 结果:{ id: number; name: string }

2.3 分布式条件类型

TypeScript
// 条件类型在联合类型上会自动分布
type ToArray<T> = T extends any ? T[] : never;

type A = ToArray<string | number>; // string[] | number[]
// 而不是:(string | number)[]

// 利用分布式特性过滤 never
type FilterNever<T> = T extends never ? never : T;

type A = FilterNever<string | number | never>; // string | number
// never 被自动过滤掉了

// 实际应用场景:清理联合类型中的无效类型
type ValidTypes = FilterNever<string | number | null | undefined | never>;
// 结果:string | number | null | undefined

三、映射类型:批量转换属性

映射类型允许我们基于现有类型创建新类型,语法类似对象字面量。

3.1 基础映射类型

TypeScript
// 将所有属性变为 readonly
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

interface User {
  id: number;
  name: string;
}

type ReadonlyUser = Readonly<User>;
// 等价于:
// type ReadonlyUser = {
//   readonly id: number;
//   readonly name: string;
// };

// 将所有属性变为可选
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// 将所有属性变为必填
type Required<T> = {
  [P in keyof T]-?: T[P];
};

3.2 属性类型转换

TypeScript
// 将所有属性转换为字符串
type Stringify<T> = {
  [P in keyof T]: string;
};

interface User {
  id: number;
  name: string;
  active: boolean;
}

type StringifiedUser = Stringify<User>;
// 结果:
// type StringifiedUser = {
//   id: string;
//   name: string;
//   active: string;
// };

// 实际应用场景:表单数据处理
interface FormData {
  username: string;
  age: number;
  subscribed: boolean;
}

// 表单提交时所有值都是字符串
type FormValues = Stringify<FormData>;

function processForm(values: FormValues): FormData {
  return {
    username: values.username,
    age: parseInt(values.age, 10),
    subscribed: values.subscribed === "true"
  };
}

3.3 属性名转换

TypeScript
// 将属性名转换为大写
type UppercaseKeys<T> = {
  [P in keyof T as Uppercase<string & P>]: T[P];
};

interface Config {
  host: string;
  port: number;
}

type UpperConfig = UppercaseKeys<Config>;
// 结果:
// type UpperConfig = {
//   HOST: string;
//   PORT: number;
// };

// 添加前缀
type WithPrefix<T, Prefix extends string> = {
  [P in keyof T as `${Prefix}${Capitalize<string & P>}`]: T[P];
};

interface User {
  name: string;
  email: string;
}

type UserWithPrefix = WithPrefix<User, "user">;
// 结果:
// type UserWithPrefix = {
//   userName: string;
//   userEmail: string;
// };

四、实战:构建类型安全的事件系统

结合以上知识,让我们构建一个类型安全的事件发布订阅系统。

4.1 事件类型定义

TypeScript
// 定义事件映射
interface EventMap {
  "user:login": { userId: string; timestamp: number };
  "user:logout": { userId: string };
  "order:created": { orderId: string; amount: number; items: string[] };
  "order:shipped": { orderId: string; trackingNumber: string };
  "error": { code: string; message: string; stack?: string };
}

// 提取所有事件名称
type EventName = keyof EventMap;

// 提取特定事件的负载类型
type EventPayload<T extends EventName> = EventMap[T];

// 事件处理器类型
type EventHandler<T extends EventName> = (
  payload: EventPayload<T>
) => void | Promise<void>;

4.2 类型安全的事件发射器

TypeScript
class TypedEventEmitter {
  private handlers: Map<EventName, Set<EventHandler<any>>> = new Map();

  on<T extends EventName>(event: T, handler: EventHandler<T>): void {
    if (!this.handlers.has(event)) {
      this.handlers.set(event, new Set());
    }
    this.handlers.get(event)!.add(handler);
  }

  off<T extends EventName>(event: T, handler: EventHandler<T>): void {
    this.handlers.get(event)?.delete(handler);
  }

  emit<T extends EventName>(event: T, payload: EventPayload<T>): void {
    this.handlers.get(event)?.forEach(handler => {
      try {
        handler(payload);
      } catch (error) {
        console.error(`Error in ${event} handler:`, error);
      }
    });
  }

  // 一次性监听
  once<T extends EventName>(event: T, handler: EventHandler<T>): void {
    const onceHandler: EventHandler<T> = (payload) => {
      this.off(event, onceHandler);
      handler(payload);
    };
    this.on(event, onceHandler);
  }
}

// 使用示例
const emitter = new TypedEventEmitter();

// 类型安全:只能监听定义的事件
emitter.on("user:login", (payload) => {
  console.log(`User ${payload.userId} logged in at ${payload.timestamp}`);
  // payload 类型:{ userId: string; timestamp: number }
});

emitter.on("order:created", (payload) => {
  console.log(`Order ${payload.orderId} created with ${payload.items.length} items`);
  // payload 类型:{ orderId: string; amount: number; items: string[] }
});

// 类型安全:发射时必须提供正确的负载
emitter.emit("user:login", { userId: "123", timestamp: Date.now() });

// 编译错误:缺少必需字段
// emitter.emit("user:login", { userId: "123" });

// 编译错误:不存在的事件
// emitter.emit("invalid:event", {});

4.3 高级:异步事件处理

TypeScript
// 支持异步处理器的类型
type AsyncEventHandler<T extends EventName> = (
  payload: EventPayload<T>
) => Promise<void>;

class AsyncEventEmitter {
  private handlers: Map<EventName, Set<AsyncEventHandler<any>>> = new Map();

  on<T extends EventName>(event: T, handler: AsyncEventHandler<T>): void {
    if (!this.handlers.has(event)) {
      this.handlers.set(event, new Set());
    }
    this.handlers.get(event)!.add(handler);
  }

  async emit<T extends EventName>(event: T, payload: EventPayload<T>): Promise<void> {
    const eventHandlers = this.handlers.get(event);
    if (!eventHandlers) return;

    await Promise.all(
      Array.from(eventHandlers).map(async handler => {
        try {
          await handler(payload);
        } catch (error) {
          console.error(`Error in async ${event} handler:`, error);
        }
      })
    );
  }
}

// 使用示例
const asyncEmitter = new AsyncEventEmitter();

asyncEmitter.on("order:created", async (payload) => {
  // 发送通知
  await sendNotification(`Order ${payload.orderId} created`);
  // 更新库存
  await updateInventory(payload.items);
  // 记录日志
  await logEvent("order_created", payload);
});

五、实用类型工具库

基于以上知识,我们可以构建自己的实用类型工具库。

5.1 DeepPartial - 深度可选

TypeScript
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? DeepPartial<T[P]> : T[P];
};

interface NestedConfig {
  database: {
    host: string;
    port: number;
    credentials: {
      username: string;
      password: string;
    };
  };
  cache: {
    enabled: boolean;
    ttl: number;
  };
}

type PartialConfig = DeepPartial<NestedConfig>;
// 所有层级的属性都变为可选

5.2 DeepReadonly - 深度只读

TypeScript
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object ? DeepReadonly<T[P]> : T[P];
};

interface State {
  user: {
    id: number;
    name: string;
  };
  settings: {
    theme: string;
    language: string;
  };
}

type ReadonlyState = DeepReadonly<State>;
// 所有层级的属性都变为 readonly

5.3 Mutable - 移除 readonly

TypeScript
type Mutable<T> = {
  -readonly [P in keyof T]: T[P];
};

interface ReadonlyConfig {
  readonly host: string;
  readonly port: number;
}

type WritableConfig = Mutable<ReadonlyConfig>;
// readonly 被移除

5.4 Nullable - 添加 null

TypeScript
type Nullable<T> = {
  [P in keyof T]: T[P] | null;
};

interface User {
  id: number;
  name: string;
  email: string;
}

type NullableUser = Nullable<User>;
// 所有属性都可以是 null

六、最佳实践与注意事项

6.1 避免过度复杂的类型

TypeScript
// ❌ 过度复杂,难以维护
type ComplexType<T> = T extends infer U 
  ? U extends (infer V)[] 
    ? V extends Promise<infer W> 
      ? W extends { data: infer X } 
        ? X 
        : never 
      : never 
    : never 
  : never;

// ✅ 分解为多个简单类型
type UnwrapArray<T> = T extends (infer U)[] ? U : T;
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type ExtractData<T> = T extends { data: infer U } ? U : T;

type ComplexType<T> = ExtractData<UnwrapPromise<UnwrapArray<T>>>;

6.2 使用类型约束提高可读性

TypeScript
// ❌ 没有约束,难以理解
type First<T> = T extends [infer U, ...any[]] ? U : never;

// ✅ 添加约束,明确意图
type First<T extends any[]> = T extends [infer U, ...any[]] ? U : never;

6.3 利用工具类型简化代码

TypeScript
// ❌ 重复定义
type UserResponse = {
  data: {
    id: number;
    name: string;
    email: string;
  };
  status: number;
};

type OrderResponse = {
  data: {
    id: string;
    amount: number;
    items: string[];
  };
  status: number;
};

// ✅ 使用泛型复用
type ApiResponse<T> = {
  data: T;
  status: number;
};

interface User { id: number; name: string; email: string; }
interface Order { id: string; amount: number; items: string[]; }

type UserResponse = ApiResponse<User>;
type OrderResponse = ApiResponse<Order>;

结语

TypeScript 的类型系统是一门深奥但强大的工具。掌握条件类型、映射类型和实用类型工具,可以让你:

  1. 编写更安全的代码 - 在编译时捕获更多错误
  2. 提高代码可维护性 - 类型即文档,清晰表达意图
  3. 减少重复代码 - 通过泛型和工具类型实现复用
  4. 增强开发体验 - 更好的自动补全和类型提示

记住,类型系统是为开发者服务的工具,而不是束缚。在追求类型安全的同时,也要保持代码的可读性和简洁性。当类型变得过于复杂时,考虑是否可以重构代码结构,或者接受一定程度的类型妥协。

持续练习这些技巧,你会发现自己对 TypeScript 的理解越来越深入,代码质量也会随之提升。

分享到:

如果这篇文章对你有帮助,欢迎请作者喝杯咖啡 ☕

加载评论中...