前置知识: TypeScript

工具类型实现原理

00:00
3 min Intermediate 2026/6/14

内置工具类型的实现与自定义

概述

TypeScript 内置了丰富的工具类型(Utility Types),它们基于映射类型、条件类型和模板字面量类型等高级特性实现。理解这些工具类型的实现原理,不仅能帮助更好地使用它们,还能为自定义工具类型提供设计思路。本文将逐一解析内置工具类型的实现,并展示如何基于相同原理构建自定义工具类型,覆盖深度操作、键提取、值类型转换等常见需求。

基础概念

映射类型(Mapped Type):基于旧类型创建新类型的语法,通过遍历键集合并变换每个属性的类型来构建新类型。语法为 { [P in K]: T }

条件类型(Conditional Type):根据类型关系选择不同结果的类型运算,语法为 T extends U ? X : Y。配合 infer 关键字可以在条件分支中提取类型。

修饰符映射类型支持 ?可选)、-?(移除可选)、readonly只读)、-readonly(移除只读)等修饰符,用于变换属性的可选性和可变性。

模板字面量类型:基于模板字符串构造类型语法`prefix${Type}suffix`,常用于生成键名变换映射类型

快速上手

属性修饰工具类型

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

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

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

// 使用示例
interface User {
  name: string;
  age: number;
  email: string;
}

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

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

属性选择工具类型

// Pick:从类型中选取部分属性
type Pick<T, K extends keyof T> = { [P in K]: T[P] };

// Omit:从类型中排除部分属性
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;

// 使用示例
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, 'title' | 'completed'>;
// { title: string; completed: boolean }

type TodoInfo = Omit<Todo, 'completed'>;
// { title: string; description: string }

详细用法

联合类型操作

// Exclude:从联合类型中排除成员
type Exclude<U, E> = U extends E ? never : U;

// Extract:从联合类型中提取成员
type Extract<U, E> = U extends E ? U : never;

// NonNullable:排除 null 和 undefined
type NonNullable<T> = T extends null | undefined ? never : T;

// 使用示例
type Status = 'active' | 'inactive' | 'pending' | null;

type ActiveStatus = Exclude<Status, null>;
// 'active' | 'inactive' | 'pending'

type NullableStatus = Extract<Status, null>;
// null

type DefiniteStatus = NonNullable<Status>;
// 'active' | 'inactive' | 'pending'

函数类型操作

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

// Parameters:获取函数参数类型元组
type Parameters<T> = T extends (...args: infer P) => any ? P : never;

// ConstructorParameters:获取构造函数参数类型
type ConstructorParameters<T> = T extends new (...args: infer P) => any ? P : never;

// InstanceType:获取构造函数实例类型
type InstanceType<T> = T extends new (...args: any[]) => infer I ? I : never;

// 使用示例
function createUser(name: string, age: number): { id: number; name: string; age: number } {
  return { id: Date.now(), name, age };
}

type UserReturn = ReturnType<typeof createUser>;
// { id: number; name: string; age: number }

type UserParams = Parameters<typeof createUser>;
// [string, number]

class DataService {
  constructor(
    private url: string,
    private timeout: number
  ) {}
  async fetch(): Promise<void> {}
}

type ServiceParams = ConstructorParameters<typeof DataService>;
// [string, number]

type ServiceInstance = InstanceType<typeof DataService>;
// DataService

Record 与 Awaited

// Record:构造键值对类型
type Record<K extends string | number | symbol, V> = { [P in K]: V };

// Awaited:递归展开 Promise
type Awaited<T> = T extends Promise<infer U> ? Awaited<U> : T;

// 使用示例
type UserRole = Record<'admin' | 'editor' | 'viewer', { permissions: string[] }>;
// {
//   admin: { permissions: string[] };
//   editor: { permissions: string[] };
//   viewer: { permissions: string[] };
// }

type DeepPromise = Promise<Promise<Promise<string>>>;
type Unwrapped = Awaited<DeepPromise>; // string

常见场景

深度工具类型

// 深度 Partial
type DeepPartial<T> = {
  [P in keyof T]?: T[P] extends object ? (T[P] extends Function ? T[P] : DeepPartial<T[P]>) : T[P];
};

// 深度 Required
type DeepRequired<T> = {
  [P in keyof T]-?: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : DeepRequired<T[P]>
    : T[P];
};

// 深度 Readonly
type DeepReadonly<T> = {
  readonly [P in keyof T]: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : DeepReadonly<T[P]>
    : T[P];
};

// 使用示例
interface Config {
  api: { baseURL: string; timeout: number };
  features: { darkMode: boolean; analytics: boolean };
}

type PartialConfig = DeepPartial<Config>;
// { api?: { baseURL?: string; timeout?: number }; features?: { darkMode?: boolean; analytics?: boolean } }

type ReadonlyConfig = DeepReadonly<Config>;
// { readonly api: { readonly baseURL: string; readonly timeout: number }; ... }

键与值提取

// 提取值类型
type ValueOf<T> = T[keyof T];

// 提取可选键
type OptionalKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? K : never;
}[keyof T];

// 提取必选键
type RequiredKeys<T> = {
  [K in keyof T]-?: {} extends Pick<T, K> ? never : K;
}[keyof T];

// 使用示例
interface Article {
  title: string;
  content: string;
  tags?: string[];
  author?: string;
}

type ArticleValues = ValueOf<Article>;
// string | string[] | undefined

type ArticleOptionalKeys = OptionalKeys<Article>;
// 'tags' | 'author'

type ArticleRequiredKeys = RequiredKeys<Article>;
// 'title' | 'content'

不可变类型

// 递归不可变
type Immutable<T> = {
  readonly [P in keyof T]: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : Immutable<T[P]>
    : T[P];
};

// 可变版本(移除所有 readonly)
type Mutable<T> = {
  -readonly [P in keyof T]: T[P] extends object
    ? T[P] extends Function
      ? T[P]
      : Mutable<T[P]>
    : T[P];
};

// 使用示例
interface State {
  readonly id: number;
  readonly items: readonly string[];
}

type MutableState = Mutable<State>;
// { id: number; items: string[] }

注意事项

  • Function 过滤:深工具类型中必须过滤 Function,否则函数属性(如 call、apply)也会被递归处理,导致类型异常。
  • 循环引用递归工具类型可能遇到循环引用。TypeScript 支持一定深递归,但过深的递归会导致编译错误
  • 性能影响:复杂的工具类型(特别是递归类型)会显著增加编译时间。在大型项目中应谨慎使用,避免过嵌套。
  • never 的传播:在条件类型中,如果条件判断结果为 never,它会在联合类型中被自动忽略。这是设计工具类型时需要考虑的行为

进阶用法

品牌 类型

// 使用交叉类型创建品牌类型
type Brand<T, B> = T & { __brand: B };

type USD = Brand<number, 'USD'>;
type EUR = Brand<number, 'EUR'>;

// 不同品牌类型不能互相赋值
const usd: USD = 100 as USD;
const eur: EUR = 100 as EUR;
// usd = eur; // 错误:不能将 EUR 赋值给 USD

// 安全的货币运算
function addUSD(a: USD, b: USD): USD {
  return (a + b) as USD;
}

addUSD(usd, usd); // 正确
// addUSD(usd, eur); // 错误:类型不匹配

类型构建器

// 构建器模式:逐步构建复杂类型
type Builder<T, Required extends keyof T = never> = {
  [K in Exclude<keyof T, Required>]?: T[K];
} & {
  [K in Required]: T[K];
} & {
  with<K extends keyof T>(key: K, value: T[K]): Builder<T, Required | K>;
  build: Required extends keyof T ? () => T : never;
};

// 简化版本
type StrictBuilder<T> = {
  [K in keyof T]?: T[K] | ((prev: T[K] | undefined) => T[K]);
};

function build<T>(spec: StrictBuilder<T>): T {
  const result = {} as T;
  for (const [key, value] of Object.entries(spec)) {
    (result as any)[key] = typeof value === 'function' ? value((result as any)[key]) : value;
  }
  return result;
}

知识检测

学习进度

-- 已学文档
--% 知识覆盖率

学习推荐

专注模式