TypeScript 为了开发者提供了基础数据类型, 同时也允许开发者使用 interface
、type
等指令自定义复杂结构类型。与编程变量类似,开发者不能无节制的新增类型,类型的整体集中管理是降低维护成本的重要手段。
对于更多复杂的类型,开发者可以基于基础类型进行衍生,TypeScript 提供了丰富的内置的工具类型,例如本文将会介绍的**Omit
**?和?Exclude
。基于这些工具类型,我们可以将基础类型组装成任意需要的样子。
假如我们面临需要从一个复杂的类型中排除某些属性,重写一个类型又增加了开发负担和后续维护成本。
假设我们需要一个 UserName
这样的类型,即排除掉 User
类型中的 id
、passwordHash
、address
interface User {
id: string;
name: string;
name1: string;
name2: string;
name3: string;
name4: string;
name5: string;
name6: string;
name7: string;
passwordHash: string;
address: string;
}
TypeScript 中 Omit
?和?Exclude
都有排除属性的能力,但是两者的使用场景和具体功能又不是完全一样,所以我们需要详细的对比两者,然后梳理出使用方式。
Exclude<T, U>
?的主要作用是从?T
?类型中排除出可以赋值给?U
?的类型,创建出一个新的子类型。比如下面的例子:
type T = 'a' | 'b' | 'c';
type U = 'a' | 'b';
type Result = Exclude<T, U>; // 结果是 'c'
说明:
在这个例子中,我们定义了两个类型?T
?和?U
,T
?是一个包含?'a'
,?'b'
,?'c'
?三个成员的联合类型,而?U
?是包含?'a'
,?'b'
?的联合类型。通过?Exclude<T, U>
?我们从?T
?中排除了可以赋值给?U
?的类型,所以结果类型?Result
?就变成了?'c'
。
Omit<T, K>
?的主要功能在于,从一个已有的对象类型?T
?中排除指定的属性?K
,进而创建一个新的对象类型。比如下面的例子:
type T = {
a: number;
b: string;
c: boolean;
};
type K = 'a' | 'b';
type Result = Omit<T, K>; // 结果是 { c: boolean }
说明:
在这个例子中,我们定义了一个对象类型?T
,包含 ‘a’, ‘b’ 和 ‘c’ 三个属性。我们希望经过处理后得到一个新的对象类型,这个类型只包含?T
?中的 ‘c’ 属性。于是我们用?Omit<T, K>
?排除了 ‘a’ 和 ‘b’ 两个属性,得到的新类型?Result
?就只包含了?{ c: boolean }
?属性。
Exclude
?和?Omit
?的主要区别在于它们处理类型的方式和侧重点。
Exclude
?是针对联合类型,用于排除一些特定成员类型;Omit
?是针对对象类型,用于忽略或排除某些特定属性;由于 User
是对象类型,并且需要排除掉排除掉 User
类型中的 id
、passwordHash
、address
,可以参考如下代码
interface User {
id: string;
name: string;
name1: string;
name2: string;
name3: string;
name4: string;
name5: string;
name6: string;
name7: string;
passwordHash: string;
address: string;
}
// 参考这个写法
type UserName = Omit<User,'id'|'passwordHash'|'address'>
在实际的编程实践中,如何选择这两个工具类型取决于我们的具体需求。如果我们希望在处理对象类型时排除掉一些属性,那么?Omit
?就是一个非常好的选择。而如果我们需要从一个类型联合中排除特定的类型,那么?Exclude
?就派上了用场。