从 TypeScript 2.8 起标准库引入了 Exclude
帮助类型, TypeScript 3.5 引入了 Omit
帮助类型。
最近在尝试使用 Omit
从一个枚举中提取它的类型子集时得不到预期的结果:
enum methods {
get = 'get',
put = 'put',
delete = 'delete',
}
type p1 = Omit<methods, methods.get>
看了一下 p1
的定义发现它是这样的:
type p1 = {
[x: number]: string;
[Symbol.iterator]: () => IterableIterator<string>;
toString: () => string;
charAt: (pos: number) => string;
charCodeAt: (index: number) => number;
concat: (...strings: string[]) => string;
... 37 more ...;
padEnd: (maxLength: number, fillString?: string | undefined) => string;
...
}
显然这是一个对象类型,我期望的是 p2 = 'put' | 'delete'
, 当使用 Exclude
时就能得到想要的结果:
type p2 = Exclude<methods, methods.get>
// equal to =
type p2 = methods.put | methods.delete
于是去研究了一下这两个方法类型。它们在标准库中的定义如下:
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
type Exclude<T, U> = T extends U ? never : T;
文档中对于 Exclude<UnionType, ExcludedMembers>
的解释是从 UnionType
排除所有在ExcludedMembers
中的联合类型 。顾名思义,Exclude
的第一个参数是一个联合类型,
而 Omit<Type, Keys>
的解释是:取 Type
的全部属性然后移除其中的 Keys
。
也就是说 Exclude
是用在联合类型上的,而 Omit
是用在对象类型或者 interface
上的。Omit
的内部使用了 Exclude
来取 Keys
。
type T1 = {
a: 'a',
b: 'b',
}
type p3 = Omit<T1, 'a'> // { b: 'b' }
type T2 = 'a' | 'b'
type P4 = Exclude<T2, 'a'> // 'b'
前面我在枚举中使用 Omit
时,枚举会被当成一个对象,事实上枚举编译后也确实是一个对象。而对枚举使用 Exclude
时,枚举值会被当成一个联合类型,从而能够得到期望的类型。同理,在非联合类型上使用 Exclude
也没有意义。