keyof用法

keyof是索引类型查询操作符。
假设T是一个类型,那么 keyof T产生的类型是T的属性名称所构成的联合类型。

  1. type Props = {color?: string; age: number}
  2. type demo = keyof Props;
  3. // demo = 'color' | 'age'
  4. type demo = keyof Props[];
  5. // demo = number | "length" | "toString" | "toLocaleString" | "pop" | "push" | "concat" | "join" | "reverse" | "shift" | "slice" | "sort" | "splice" | "unshift" | "indexOf" | "lastIndexOf" | ... 14 more ... | "includes"

也可以操纵类:

class Person{
    name: string;
  age: number;
}

type demo1 = keyof Person;  // 'name' | 'age'
type demo2 = keyof Person[];  // 同上访问数组的类型

也支持基本数据类型;

type k1 = keyof boolean;
// k1 = 'valueOf'

type k2 = keyof string;
// k2 = number | "toString" | "charAt" | "charCodeAt" | "concat"  等等...

type k3 = keyof number;
// k3 = "toString" | "toFixed" | "toExponential" | "toPrecision" | "valueOf" | "toLocaleString"

此外,keyof 也称为输入索引类型查询,也称为查找类型。
在语法上我们看起来像是属性的访问,单最终会被转换为类型;

// 这里我们引用上面的Person类
type k1 = Person['name']   // 'string'
type k2 = Person['name' | 'age']   // 'string' | 'number'

typeof

typeof 用于获取变量的类型。在ts中这个操作符后面始终是一个变量。

const Person = {name: 'my name', age: 18};
type P = typeof Person;   // type P = {name: string; age: number}
type P1 = keyof typeof Person;   // type P1 = 'name' | 'age'

思维延伸

此前因为始终不能理解 utility-types 的工具源码实现,这里解释一下这个方法的具体实现步骤。

export type RequiredKeys<T extends object> = {
  [P in keyof T]-?: {} extends Pick<T, P> ? never : P
}[keyof T]

我们以 RequiredKeys 为案例,从文章中我们知道用法:

type demo = RequiredKeys<{color?: string; age: number, b: boolean}>
// type demo = 'age' | 'b'

但是怎么只取 key 的呢?ts中又是怎么遍历的呢?

让我们来看下面的示例:

// 理解方法
{color?: string, age: number, s: number}['color' | 'age' | 's']
// 这里会索引类型
// 这样我们就得到了 string | number | undefined

综合以上知识,我们再来分析源码:

// 假设 T = {color?: string, age: number, s: number}
// 分析步骤

1、{ [P in keyof T]: P; }[keyof T]

2、{ [P in keyof T]: P; }['color' | 'age' | 's'] 

3、{color?: 'color', age: 'age', s: 's'}['color' | 'age' | 's']   
// 返回 'color' | 'age' | 's' | undefined

通过上面的示例,我们大概知道了 {}[keyof] 的具体实现了吧。
想完全实现 RequiredKeys ,还要用 -? 强制转换为必选,去掉undefined,再用 Pick 过滤掉,就可以得到想要的效果了。