1. 前言
前两篇文章中梳理了一下TypeScript中的基础类型与高级类型,除此之外呢,TypeScript还内置了很多的工具类型。接下来我们来一起看看他们的作用到底是什么呢?
2. 属性修饰工具类型
属性修饰工具类型,其实就是对属性的一个修饰,包括对象属性和数组元素的可选、必选、只读、可写等。它包含了Partial
、Required
、Readonly
三个工具类型,这几个类型主要使用了属性修饰
、映射类型
与索引类型
Partial
将所有属性变为可选属性,也就是为每个属性都添加上?
:
type Partial<T> = {
[P in keyof T]?: T[P];
};
Required
将所有属性变为必选属性,它与Partial
刚好相反:
type Required<T> = {
[P in keyof T]-?: T[P];
};
-?
的含义就是移除可选属性。
Readonly
将所有属性变为只读属性,它会为类型对象的每一项都加上前缀readonly
:
type Readonly<T> = {
readonly [P in keyof T]: T[P];
};
在TypeScript的内置类型中,Readonly
并没有与其相反的类型。但是我们可以自己来实现一个工具类型,将属性中的readonly修饰移除:
type Mutable<T> = {
-readonly [P in keyof T]: T[P];
}
3. 结构工具类型
结构工具类型可以分为结构声明
和结构处理
。它包含了Record
、Pick
、Omit
三个类型,其中Record属于结构声明,Pick和Omit属于结构处理,它们主要使用条件类型、映射类型以及索引类型。
Record
将K的每一个属性都定义为T类型:
type Record<K extends keyof any, T> = {
[P in K]: T;
}
其中,K extends keyof any
即为键的类型,这里使用extends keyof any
标明,传入的K可以是单个类型,也可以是联合类型,而T则为属性的类型。
// 键名均为字符串,键值类型未知
type Record1 = Record<string, unknown>;
// 键名均为字符串,键值类型为任意类型
type Record2 = Record<string, any>;
// 键名均为字符串,键值类型为string
type Record3 = Record<string, string>;
interface IPersonInfo {
name: string;
age: number;
}
type IdentityName = 'student' | 'teacher';
const students: Record<IdentityName, IPersonInfo> = {
student: { name: 'Yancy', age: 26 },
teacher: { name: 'Jeeny', age: 30 },
};
在有的工具函数库中,还有类似的一些结构声明工具类型实现:
type Dictionary<T> = {
[index: string]: T;
};
type NumericDictionary<T> = {
[index: number]: T;
};
Pick
将某个类型的子属性筛选出来,组合成一个新的类型:
type Pick<T, K extends keyof T> = {
[P in K]: T[P];
}
它接受两个泛型参数,T
是我们进行结构处理的原类型,一般来说都是对象类型,而K
则被约束为T
类型的键名联合类型。我们在上一篇文章中提到过,in
关键字类型于数组的map
方法。
interface IPerson = {
name: string;
age: number;
sex: string;
}
type NameAndAge = Pick<IPerson, 'name' | 'age'>;
// NameAndAge相当于定义了一下类型
interface NameAndAge {
name: string;
age: number;
}
Omit
将传入的键从类型中移除。它与Pick
相反,Pick是保留传入的键,而Omit则是移除传入的键。
type Omit<T, K extends keyof any> = Pick<T, Exclude<keyof T, K>>;
我们可以看出,Omit是基于Pick来实现的,只是在传入键的地方做了一个反向操作。Exclude<keyof T, K>
就是在T的键名联合类型中剔除掉K的部分。
interface IPerson {
name: string;
age: number;
sex: string;
isStudent: boolean;
}
type IPersonInfo = Omit<IPseron, 'isStudent'>;
type IPersonStatus = Omit<IPerson, 'name' | 'age' | 'sex'>;
4. 集合工具类型
集合工具类型,故名思义,它是用于对多个类型取并集、交集等,它包含了Extract
、Exclude
、NonNullable
。在开始之前呢,我们先回顾一下数学中的一些集合概念:
- 并集:给定两个集合A,B,把他们所有的元素合并在一起组成的集合,叫做集合A与集合B的并集;
- 交集:给定两个集合A,B,由所有属于集合A且属于集合B的元素所组成的集合,叫做集合A与集合B的交集
- 差集:给定两个集合A,B,则所有属于A且不属于B的元素构成的集合,叫做集合A减集合B(或集合A与集合B之差),类似地,对于集合A、B,把集合{x∣x∈A,且x∉B}叫做A与B的差集。
- 补集:补集一般指绝对补集,即一般地,设S是一个集合,A是S的一个子集,由S中所有不属于A的元素组成的集合,叫做子集A在S中的绝对补集。
Extract
Extract,意为提取,提炼。对应数学中的交集
:
type Extract<T, U> = T extends U ? T : never;
提取T
中可赋值给U
的所有联合成员来组合成一个新的类型。
type T0 = Extract<'a' | 'b' | 'c', 'a' | 'd'>;
// 等同于 type T0 = 'a';
type T1 = Extract<string | number | (() => void), Function>;
// 等同于type T1 = () => void
Exclude
将某个子类型中的子属性剔除。它和Extract
刚好相反,对应数学中的差集
:
type Exclude<T, U> = T extends U ? never : T;
从T
中剔除所有可分配给U
的联合成员来组合成一个新的类型。
type T0 = Exclude<"a" | "b" | "c", "a">;
// 等同于 type T0 = "b" | "c"
type T1 = Exclude<string | number | (() => void), Function>;
// 等同于 type T1 = string | number
NonNullable
排除类型中的null和undefined来构造一个新的类型:
type NonNullable<T> = T extends null | undefined ? never : T;
从T
中将类型为null或者是undefined的属性全部剔除:
type T0 = NonNullable<string | number | undefined>;
// 等同于type T0 = string | number
5. 模式匹配工具类型
模式匹配工具类型,包含了Parameters
、ReturnType
、ConstructorParameters
、InstanceType
,它们主要使用条件类型和infer关键字,前两个是针对于函数类型签名的模式匹配,后两个是针对Class
进行的模式匹配。
Parameters
用于提取函数参数的类型:
// 函数的通用类型签名
type FunctionType = (...args: any) => any;
type Parameters<T extends FunctionType> = T extends (...args: infer P) => any ? P : never;
type T0 = Parameters<() => string>; // type T0 = []
type T1 = Parameters<(s: string) => void>; // type T1 = [s: string]
type T2 = Parameters<any>; // type T2 = unknown[]
我们还可以在此基础上进一步扩展,例如只匹配第一个参数的类型:
type FirstParameters<T extends FunctionType> = T extends (
arg: infer P,
...args: any
) => any
? P
: never;
type FuncFoo = (arg: number) => void;
type FuncBar = (...args: string[]) => void;
type FooFirstParameter = FirstParameter<FuncFoo>; // number
type BarFirstParameter = FirstParameter<FuncBar>; // string
ReturnType
用于提取函数返回值的类型:
type ReturnType<T extends FunctionType> = T extends (...args: any) => infer R ? R : any;
```typescript
type T0 = ReturnType<() => string>; // type T0 = string
type T1 = ReturnType<(s: string) => void>; // type T1 = void
ConstructorParameters
用于获取类的构造函数的参数类型,存储到一个元组中:
// Class的通用类型签名
type ClassType = abstract new (...args: any) => any;
type ConstructorParameters<T extends ClassType> = T extends abstract new (
...args: infer P
) => any
? P
: never;
InstanceType
用于构造由构造函数的实例组成的类型:
type InstanceType<T extends ClassType> = T extends abstract new (
...args: any
) => infer R
? R
: any;
6. 结语
以上已经囊括了TypeScript中的大部分内置类型,还有一部分后面再补充吧~跟着敲了一遍,对于之前一些不太理解的类型也更加清晰了,留此记录,方便后期查阅,希望也能够为其他前端er提供一些帮助。我是一只小菜鸟,不足之处,还请大家多多指教。