作者: Marius Schulz
原文链接: https://mariusschulz.com/blog/the-omit-helper-type-in-typescript
3.5 版本之后,TypeScript 在 lib.es5.d.ts 里添加了一个 Omit
帮助类型。Omit
类型让我们可以从另一个对象类型中剔除某些属性,并创建一个新的对象类型:
type User = {
id: string;
name: string;
email: string;
};
type UserWithoutEmail = Omit
// 等价于:
type UserWithoutEmail = {
id: string;
name: string;
};
而在 lib.es5.d.ts 里面 Omit
帮助类型长这样:
_/**_
_* Construct a_ _type_ _with the properties of T except_ _for_ _those in_ _type_ _K._
_ */_
type Omit
把这个类型定义解释清楚并且明白其中的原理,我们可以试着实现一个自己的版本,来还原它的功能。
定义 Omit 帮助类型
我们先从上面的那个 User 类型开始:
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
帮助类型来做这件事:
type UserKeysWithoutEmail = Exclude
_// 等价于:_
type UserKeysWithoutEmail = Exclude<
"id" | "name" | "email",
"email"
>;
_// 等价于:_
type UserKeysWithoutEmail = "id" | "name";
而 Exclude
在 lib.es5.d.ts 里面是这样定义的:
/**
* Exclude from T those types that are assignable to U
*/
type Exclude
它用了一个条件类型和 never 类型。用 Exclude
实际上我们在从联合类型"id" | "name" | "email"
中去掉那些匹配 "email"
类型的类型。而匹配 "email"
类型的只有 "email"
,所以就剩下了· "id" | "name" 。
最后,我们需要创意一个对象类型,包含 User 类型属性子集的对象类型。其实更具体的说,就是要创建一个对象类型,它的属性都是在联合类型 UserKeysWithoutEmail 中的。我们可以用 Pick
type UserWithoutEmail = Pick
// 等价于:
type UserWithoutEmail = Pick
// 等价于:
type UserWithoutEmail = {
id: string;
name: string;
};
而 Pick
/**
* From T, pick a set of properties whose keys are in the union K
*/
type Pick
[P in K]: T[P];
};
Pick
现在,我们来把上面提到的 keyof ,Exclude
type UserWithoutEmail = Pick
值得注意的是这样的写法只能应用到我们定义的 User 类型中。我们加入一个范型,就能让它用在其他地方了:
type Omit
现在,我们可以计算出我们的 UserWithoutEmail 类型了:
type UserWithoutEmail = Omit
因为对象的键只能是字符串、数字或 Symbol,那么我们可以给 K 加个约束条件:
type Omit
这样直接约束 extends string | number | symbol 看上去有点啰嗦了。我们可以用 keyof any 来实现,因为它们是等价的:
type Omit
于是我们现在完成了!我们已经实现了 lib.es5.d.ts 中定义的 Omit
_/**_
_* Construct a_ _type_ _with the properties of T except_ _for_ _those in_ _type_ _K._
_ */_
type Omit
拆解 Omit
下面这段代码就是逐步拆解的 Omit
type User = {
id: string;
name: string;
email: string;
};
type UserWithoutEmail = Omit
// 等价于:
type UserWithoutEmail = Pick<
User,
Exclude
>;
// 等价于:
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
// 等价于:
type UserWithoutEmail = Pick
// 等价于:
type UserWithoutEmail = {
[P in "id" | "name"]: User[P];
};
// 等价于:
type UserWithoutEmail = {
id: User["id"];
name: User["name"];
};
// 等价于:
type UserWithoutEmail = {
id: string;
name: string;
};
齐活,我们的 UserWithoutEmail 类型。