likes
comments
collection
share

TypeScript学习之Omit

作者站长头像
站长
· 阅读数 10

Omit是什么

OmitTypeScript3.5新增的一个辅助类型,它的作用主要是:以一个类型为基础支持剔除某些属性,然后返回一个新类型。

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

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

// PersonWithoutLocation equal to QuantumPerson
type QuantumPerson = {
    name: string;
    age: string;
};

Omit背后定义

那接下来我们看看Omit内部是如何实现的。

// lib.es5.d.ts
/**
 * 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>>;

接下来就是理解下这段代码。

keyof运算符

keyof诞生于TypeScript2.1版本,它的作用是:帮助我们获取某种类型的所有键,返回的是一个联合类型。 s 我们可以看一个很常见的示例:

function getProperty<T, K extends keyof T>(obj: T, key: K) {
  return obj[key]; // Inferred type is T[K]
}

function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]) {
  obj[key] = value;
}

Exclude

用法:Exclude<Type, ExcludedUnion>。简单理解Exclude就是数学集合中找出Type的“差集”,就是将类型A与B对比,返回A中独有的类型。

看一个示例:

TypeScript学习之Omit

那么大家肯定也会好奇Exclude背后的定义是怎么样的。

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

Extends

T extends U ? never : T这里的extends可与class的extends不是一回事,这里指的是条件类型。

这里不做过多的扩展,重点通过一个概念分布式条件类型来理解上面Exclude的写法。

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

type C = Exclude<A, B>; // 'b' | 'c';

// A extends B ? never : A 等价于 ('a' | 'b' | 'c') extends B ? never : ('a' | 'b' | 'c') 等价于如下
type D = ('a' extends B ? never : 'a') | ('b' extends B ? never : 'b') | ('c' extends B ? never : 'c'); // 'b' | 'c';

Pick

Pick<Type, Keys>,顾名思义:拣选属性,将 Type 的部分类型 Keys 挑出来,返回这部分类型。

其背后的定义就比较好理解了:

// 通过keyof操作符+索引类型T[P]实现
/**
 * 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];
};

怎么推导Omit

通过上面前置知识,相信很好的理解了type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;。 下面我们一步步推导上述“公式”加深知识的理解。

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

type PersonWithoutLocation = Omit<Person, 'location'>;
// 推导
type PersonWithoutLocation = Pick<Person, Exclude<'name' | 'age' | 'location', 'location'>>;
// 推导
type PersonWithoutLocation = Pick<Person, (('name' extends 'location' ? never : 'name') | ('age' extends 'location' ? never : 'age') | ('location' extends 'location' ? never : 'location'))>;
// 推导
type PersonWithoutLocation = Pick<Person, ('name' | 'age' | never)>;
// 推导
type PersonWithoutLocation = Pick<Person, ('name' | 'age')>;
// 推导
type PersonWithoutLocation = {
    [p in 'name' | 'age']: Person[p]
};
// 推导
type PersonWithoutLocation = {
    name: string;
    age: string;
}

Ending🌹