likes
comments
collection
share

深入 TS 高级类型 Omit,轻松把没用的东西剔除掉 🫣🫣🫣Omit 是 TypeScript 提供的一个高级类

作者站长头像
站长
· 阅读数 6

Omit 是 TypeScript 提供的一个高级类型工具,用于从一个类型中排除指定的键,从而构造一个新的类型。它在处理对象类型时非常有用,可以帮助你轻松创建不包含某些特定属性的类型。

它的主要使用场景有以下几个方面:

  1. 去除不需要的属性:当你有一个类型,但不需要其中的一些属性时,可以使用 Omit 去除这些属性。

  2. 创建子类型:从一个大型类型中派生出较小的子类型,只包含某些特定的属性。

Omit 的基本使用

假设我们有一个用户类型 User:

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

我们可以使用 Omit 创建一个新的类型,该类型不包含 password 属性:

type UserWithoutPassword = Omit<User, "password">;

const user: UserWithoutPassword = {
  id: 1,
  name: "Moment",
  email: "moment@qq.com",
};

深入 TS 高级类型 Omit,轻松把没用的东西剔除掉 🫣🫣🫣Omit 是 TypeScript 提供的一个高级类

我们还可以排除多个属性,只需将属性名用联合类型 | 连接起来:

type BasicUserInfo = Omit<User, "email" | "password">;

const basicUser: BasicUserInfo = {
  id: 1,
  name: "moment",
};

深入 TS 高级类型 Omit,轻松把没用的东西剔除掉 🫣🫣🫣Omit 是 TypeScript 提供的一个高级类

Omit 的基本实现

Omit 的定义如下:

type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

在上面的代码中 T 是一个泛型,代表一个对象类型。K 是一个泛型,代表要从 T 中剔除的键。K 必须是 keyof any 的子集,也就是说,K 可以是任何类型的键。

Pick<T, Exclude<keyof T, K>> 从类型 T 中挑选出 Exclude<keyof T, K> 中的键。

进阶使用

动态类型与 Omit 结合使用

结合条件类型和 Omit 可以实现更加动态的类型操作。

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

type ConditionalOmit<T, K extends keyof any, U> = T extends U ? Omit<T, K> : T;

type AdminUser = User & { admin: true };
type RegularUser = User & { admin?: false };

type UserWithoutPassword<T> = ConditionalOmit<T, "password", { admin?: false }>;

const adminUser: UserWithoutPassword<AdminUser> = {
  id: 1,
  name: "moment",
  email: "moment@qq.com",
  password: "moment",
  admin: true,
};

const regularUser: UserWithoutPassword<RegularUser> = {
  id: 2,
  name: "m",
  email: "m@qq.com",
  // password 被省略
};

在上面的这些代码中包含了一个条件类型 ConditionalOmit:

type ConditionalOmit<T, K extends keyof any, U> = T extends U ? Omit<T, K> : T;

它的主要作用是用于根据某些条件有选择性地移除类型中的属性:

  1. T 是输入类型。

  2. K 是要移除的属性。

  3. U 是条件类型。

条件类型 T extends U ? Omit<T, K> : T 的含义是:

  1. 如果 T 可以赋值给类型 U,则从 T 中移除属性 K(通过 Omit<T, K> 实现)。

  2. 否则,保留类型 T 不变。

最终实现有条件移除 Password 属性的类型 UserWithoutPassword:

  1. 如果 T 满足 { admin?: false } 条件(即普通用户),则移除 T 中的 password 属性。

  2. 否则(即管理员用户),保留 T 不变。

递归 Omit

通过递归类型实现嵌套类型的 Omit 操作。

interface NestedObject {
  id: number;
  details: {
    name: string;
    email: string;
    address: {
      street: string;
      city: string;
    };
  };
}

type DeepOmitHelper<T, K extends keyof any> = {
  [P in keyof T]: P extends K
    ? never
    : T[P] extends infer TP
    ? TP extends object
      ? DeepOmit<TP, K>
      : TP
    : never;
};

type DeepOmit<T, K extends keyof any> = Pick<
  DeepOmitHelper<T, K>,
  {
    [P in keyof DeepOmitHelper<T, K>]: DeepOmitHelper<T, K>[P] extends never
      ? never
      : P;
  }[keyof DeepOmitHelper<T, K>]
>;

type NestedWithoutEmail = DeepOmit<NestedObject, "email">;

const nestedObject: NestedWithoutEmail = {
  id: 1,
  details: {
    name: "moment",
    address: {
      street: "中山大道",
      city: "珠海",
    },
  },
  // email 被省略
};

对于上面的代码主要有以下几个方面的解释:

  1. DeepOmitHelper 类型:

    • DeepOmitHelper 是一个辅助类型,用于处理嵌套对象。

    • 对于每个属性 P,如果 P 是要删除的属性 K,则将其类型设置为 never。

    • 如果属性 P 是一个对象,则递归应用 DeepOmit。

    • 否则,保持属性类型不变。

  2. DeepOmit 类型:

    • 使用 Pick 和映射类型来删除被设置为 never 的属性。

    • Pick<DeepOmitHelper<T, K>, {...}> 从 DeepOmitHelper 中挑选出所有非 never 的属性键,创建一个新的类型。

最终实现 NestedWithoutEmail 类型是从 NestedObject 中递归删除 email 属性后的结果。

使用 Omit 构建 REST API 响应类型

在构建 REST API 时,常常需要对请求和响应的类型进行严格的控制。

interface User {
  id: number;
  name: string;
  email: string;
  password: string;
  createdAt: Date;
  updatedAt: Date;
}

// 创建一个请求体类型,省略 id 和时间戳
type CreateUserRequest = Omit<User, "id" | "createdAt" | "updatedAt">;

const newUser: CreateUserRequest = {
  name: "moment",
  email: "moment@qq.com",
  password: "moment",
};

// 创建一个响应体类型,省略 password
type UserResponse = Omit<User, "password">;

const userResponse: UserResponse = {
  id: 1,
  name: "moment",
  email: "moment@qq.com",
  createdAt: new Date(),
  updatedAt: new Date(),
  // password 被省略
};

总结

Omit 是一个非常强大的工具类型,通过结合条件类型、联合类型、递归类型和映射类型等高级类型特性,可以实现复杂的类型操作。它在很多场景下都非常有用,例如创建请求和响应类型、动态属性过滤、构建嵌套对象类型等。通过灵活运用 Omit,可以使 TypeScript 代码更加灵活和类型安全,提高代码的可维护性和可读性。

转载自:https://juejin.cn/post/7384266820466245684
评论
请登录