4,313

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天, 点击查看活动详情

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

keyof

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

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

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

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

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

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

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

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

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

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

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

类型操作实战

Partial & Required

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

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

type Partial<T> = {
    [P in keyof T]?: T[P]

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

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

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>'.

实现思路和前面相似

type Partial<T> = {
    [P in keyof T]-?: T[P]

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