TypeScript - 理清Omit与Exclude的关系与区别

5,613 阅读3分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第20天,点击查看活动详情

大家好,我是半夏👴,一个刚刚开始写文的沙雕程序员.如果喜欢我的文章,可以关注➕ 点赞 👍 加我微信:frontendpicker,一起学习交流前端,成为更优秀的工程师~关注公众号:搞前端的半夏,了解更多前端知识! 点我探索新世界!

typescript2.8中开始支持Exclude,他的用法是从联合类型UnionType中排除一个类型ExcludedMembers

Exclude<UnionType, ExcludedMembers>

PS: 这里官方文档给出的用法是在联合类型中

在官方文档中是这样定义Exclude的type Exclude<T, U> = T extends U ? never : T;

从定义来看,判断T是否继承U,如果是返回never否则返回T,但是这样说其实也不正确,返回的并不是完整的T,并且这个Exclude也不是你想像中的。

PS:never 类型表示的是那些永不存在的值的类型

例如:函数是会抛出异常的函数,因此永远不会正常返回值,所以可以定义该函数的返回值为 never;

例如:函数是一个 while(true){} 的函数,因此会陷入永久循环,也不会正常返回值,所以可以定义该函数的返回值为 never。

例如下面这个例子:

type A = 'a' | 'b' | 'c';
type B = 'a' | 'd';


type C = Exclude<A, B>; 

你以为的Exclude可能是这样的:

type c ='a' | 'b' | 'c'; extends 'a' | 'd'; ? never : 'a' | 'b' | 'c';

其实是这样子的:

type c =
   ('a' extends 'a' | 'd'  ? never : 'a')
  | ("b" extends 'a' | 'd'  ? never : 'b')
  | ("c" extends 'a' | 'd'  ? never : 'c')

这里C的类型是'b' | 'c';相当于A不在B中的类型!所以叫排除B。

image-20220427233456151

例如下面这个例子

typescript3.5中支持了Omit。他的用法是从类型Type中排除一组键(字符串文字或字符串文字的联合)

Omit<Type, Keys>

type Person = {
    name: string;
    age: string;
    location: string;
};

type PersonWithoutLocation = Omit<Person, 'location'>;

通过运行官方文档的例子,我们可以发现Omit的作用是:以一个定义好的类型位基础,踢出指定的属性,重新生成一个类型!

例如上面的PersonWithoutLocation排除了location属性,最后得到的结果就是

{
   	name: string;
    age: string;
 }

image-20220427232102234

一开始我以为,Omit可以用在联合类型中排除属性,但是后来试了好像不太行啊!

例如我有这样一个联合文字类型:

type unionType = 'a' | 'b' | 'c' | 'd';

我想创建这样一个类型:

type mapType = {
  a: string;
  b: string;
  c: string;
  d: string;
}

使用unionType,完全可行!

type mapType = {
    [k in unionType]: string;
}

image-20220428001130551

但是mapType突然不需要d了,使用Omit。

type mappedTypeWithOmit = {
    [k in Omit<something, "def">]: string;
}

但是TS直接报错了!报错说Omit的结果无法分配给string类型

image-20220428001330473

如果我们单独的看:

type unionType = 'a' | 'b' | 'c' | 'd';

type c= Omit<unionType, "d">

最终得到的c是一个字符串数组类型啊

image-20220428001613371

我看了一下Omit的定义:

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

请注意Exclude<keyof T, K>>中keyof,这里的keyofinterface 的键!也就是说你的T是一个interface。所以我觉得Omit是无法用在联合类型上的。

keyo如果用来取联合类型的话,会得到下面的结果。

image-20220428002723333

type A='a' | 'b' | 'c' | 'd'

type C =keyof  A;