0%

TypeScript类型体操中的关键字详解(一)

TypeScript类型操作中typeofin经常放在一起使用,使用频率也很高,因此将这两个关键字放在一起介绍。

keyof

使用

keyof操作符接受一个对象类型作为参数,返回该对象属性名组成的字面量联合类型

1
2
type Dog = { name: string; age: number;  };
type D = keyof Dog; //type D = "name" | "age"

在一些高级类型中经常会用到keyof any, 这又是什么鬼?鼠标放上去看看就知道了
image.png

可以看到keyof any 返回的是一个联合类型:string | number | symbol,结合前文说到keyof是为了取得对象的key值组成的联合类型,那么key值有可能是什么类型呢?自然就是string | number | symbol

该关键字一般会和extends关键字结合使用,对对象属性的类型做限定,比如K extends keyof any就代表K的类型一定是keyof any所返回的联合类型的子类,如果输入不符合限定,那么自然也就不能作为对象的属性,类型系统就会报错。

因此,keyof any 表示了对象key值可能的取值类型。这一点在本文之后的一些类型实现中也会用到。

注意点

遇到索引签名时,typeof会直接返回其类型

1
2
3
4
5
6
7
8
type Dog = {  [y:number]: number  };
type dog = keyof Dog; //type dog = number

type Doggy = { [y:string]: boolean };
type doggy = keyof Doggy; //type doggy = string | number

type Doggy = { [y:string]: unknown, [x:number]: boolean};
type doggy = keyof Doggy; //type doggy = string | number

可以看到索引类型为number时,keyof 返回的类型是string | number, 这是因为JavaScript的对象属性会默认转换为字符串。

in

in的右侧一般会跟一个联合类型,使用in操作符可以对该联合类型进行迭代。
其作用类似JS中的for...in或者for...of

1
2
3
4
5
6
7
8
9
10
type Animals = 'pig' | 'cat' | 'dog'

type animals = {
[key in Animals]: string
}
// type animals = {
// pig: string; //第一次迭代
// cat: string; //第二次迭代
// dog: string; //第三次迭代
// }

类型操作实战

Partial & Required

Partial:将某个类型里的属性全部变为可选项

思路是通过泛型传入待处理类型,先用keyof取到所给类型所有属性组成的字面量联合类型,然后使用in进行遍历,同时结合 ?操作符,将每个属性变成可选的

1
2
3
type Partial<T> = {
[P in keyof T]?: T[P]
}

[P in keyof T]这段代码表示遍历T中的每一个属性,那么T[P]就是每个属性所对应的值,可以简单理解为前者取的是键key,后者取的是值value

Required:和Partial的作用相反,是为了将某个类型里的属性全部变为必选的

1
2
3
4
5
6
7
8
9
interface Props {
a?: number;
b?: string;
}

const obj: Props = { a: 5 }; // b是可选的,因此缺少这个属性也可以

const obj2: Required<Props> = { a: 5 }; // 通过Required将属性变为必选的,等号右边对象缺少这个属性,因此赋值失败
//Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.

实现思路和前面相似

1
2
3
type Partial<T> = {
[P in keyof T]-?: T[P]
}

上文对应的-?代表着去掉可选,与之对应的还有+?,两者正好相反