TypeScript 学习笔记(六)- Omit

3.5 版本之后,TypeScript 加了一个 ​Omit<T, K>​ 类型。
​Omit<T, K>​ 类型可以从继承的对象属性中剔除某些属性。

type User = {
  id: string;
  name: string;
  email: string;
};

type UserWithoutEmail = Omit<User, "email">;

// 等价于
type UserWithoutEmail = {
  id: string;
  name: string;
};

Omit<T, K> 在 lib.es5.d.ts 文件里面是这样定义的:

构造一个除了类型 K 之外的具有 T 属性的类型

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;

把这个类型定义解释清楚并且明白其中的原理,我们可以试着实现一个自己的版本,来还原它的功能。

定义 Omit<T, K> 帮助类型
type User = {
  id: string;
  name: string;
  email: string;
};

首先,我们需要遍历 ​User​ 类型中的属性名。我们可以用 ​keyof​ 操作符来获取一个包含所有对象属性的字符串集合:

type UserKeys = keyof User;

// 等价于
type UserKeys = "id" | "name" | "email";

然后需要将字符串集合中一些属性取出。
在 ​User​ 类型中,需要从 ​"id" | "name" | "email"​中去掉 ​"email"​。可以使用 ​Exclude<T, U>​ 来做这件事:

type UserKeysWithoutEmail = Exclude<UserKeys, "email">;

// 等价于
type UserKeysWithoutEmail = Exclude<"id" | "name" | "email", "email">;

// 等价于
type UserKeysWithoutEmail = "id" | "name";

​Exclude<T, U>lib.es5.d.ts 文件里面是这样定义的:

/**
 * Exclude from T those types that are assignable to U
 */
type Exclude<T, U> = T extends U ? never : T;

它用了一个条件类型​never​ 类型。用​Exclude<T, U>​ 可以从集合​"id" | "name" | "email"​中去掉那些匹配 ​"email"​类型的类型。而匹配 ​"email"​类型的只有它自己,所以就剩下了​"id" | "name"​

最后,我们需要创建一个包含 ​User​ 类型属性子集的对象类型。具体地说,就是创建一个只包含 UserKeysWithoutEmail类型的对象,可以用 ​Pick<T, K>​ 来挑出User类型中对应的属性名。

type UserWithoutEmail = Pick<User, UserKeysWithoutEmail>;

// 等价于
type UserWithoutEmail = Pick<User, "id" | "name">;

// 等价于
type UserWithoutEmail = {
  id: string;
  name: string;
};

​Pick<T, K>lib.es5.d.ts 文件里面是这样定义的

/**
 * From T, pick a set of properties whose keys are in the union K
 */
type Pick<T, K extends keyof T> = {
  [P in K]: T[P];
};

​Pick<T, K>​是一个映射类型,它用了​keyof​ 操作符和一个索引类型 ​T[P]​来获取类型对象类型 ​T​ 中的属性 ​P​ 。
现在,我们来把上面提到的 ​keyof​,​Exclude<T, U>​ 和 ​Pick<T, K>​整合成一个类型

type UserWithoutEmail = Pick<User, Exclude<keyof User, "email">>;

值得注意的是这样的写法只能应用到我们定义的 ​User​ 类型中。加入一个范型就能让它用在其他地方了

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

现在,可以计算出 ​UserWithoutEmail​ 类型

type UserWithoutEmail = Omit<User, "email">;

因为对象的键只能是字符串、数字或 Symbol,那么我们可以给 ​K​ 加个约束条件

type Omit<T, K extends string | number | symbol> = Pick<T, Exclude<keyof T, K>>;

这样直接约束 ​​extends string | number | symbol​​ 看上去有点啰嗦了。我们可以用 ​​keyof any​​ 来实现,因为它们是等价的

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

这样就实现了 lib.es5.d.ts 中定义的​Omit<T, K>​​ 类型

/**
 * Construct a type with the properties of T except for those in type K.
 */
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
拆解 Omit<User, “email”>

下面这段代码就是逐步拆解的 ​Omit<User, “email”>​ 类型。试着跟随每个步骤并理解 TypeScript 是如何计算出最终的类型的

type User = {
  id: string;
  name: string;
  email: string;
};

type UserWithoutEmail = Omit<User, "email">;

//  等价于
type UserWithoutEmail = Pick<User, Exclude<keyof User, "email">>;

//  等价于
type UserWithoutEmail = Pick<User, Exclude<"id" | "name" | "email", "email">>;

//  等价于
type UserWithoutEmail = Pick<
  User,
  | ("id" extends "email" ? never : "id")
  | ("name" extends "email" ? never : "name")
  | ("email" extends "email" ? never : "email")
>;

//  等价于
type UserWithoutEmail = Pick<User, "id" | "name" | never>;

//  等价于
type UserWithoutEmail = Pick<User, "id" | "name">;

//  等价于
type UserWithoutEmail = {
  [P in "id" | "name"]: User[P];
};

// This is equivalent to:
type UserWithoutEmail = {
  id: User["id"];
  name: User["name"];
};

// This is equivalent to:
type UserWithoutEmail = {
  id: string;
  name: string;
};

参考链接
The Omit Helper Type in TypeScript
TypeScript 中的 Omit 帮助类型[中译]

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值