Typescript中的keyof和typeof

399 阅读2分钟

1.keyof

keyof的作用是用来遍历ts中某些类型的属性,例如对象类型,接口类型,类等,例如

interface response {
  data: [];
  code: number;
  location: string
}
type keys = keyof response 
// keyof response返回的是一个字符串组成字面量类型,即等价于:
// type keys = 'data' | 'code' | 'location',keys只能是这三个字符串中的其中一个

class Person {
  name: string = 'liming'
  age: number = 123
}
type classKeys = keyof Person // 'name' | 'age'
let key1: classKeys = 'name'
key1 = 'laoction' // ts报错

所以看见keyof就知道大概是想获取对象、接口、类的属性名组成的字符串别名。

1.1 keyof的作用

我们通常会在函数中的参数中获取对象的属性值

function getValue(obj: object, key: string) {
    return obj[key] // 这时候会报ts错误,显示Element implicitly has an 'any' type because expression of type 'string' can't be used to index type '{}'.
}

主要问题是ts不知道我们输入的属性是否在对象中已经存在,所以我们告诉ts,属性名在对象中已经存在。

function getValue<T extends object, key extends keyof T>(obj: T, params: K) {
    return obj[params]
}
// 但是这个时候ts还是报错,主要的报错原因在object继承上,
// 报错:Don't use `object` as a type. The `object` type is currently hard to use ([see this issue]
// Consider using `Record<string, unknown>` instead, as it allows you to more easily inspect and use the keys @typescript-eslint/ban-types


// 修正如下:
function getValue<T extends Record<string, unknown>, key extends keyof T> (obj: T, params: key) {
  return obj[params]
}

这里是通过使用Record类型(TS内置的高级类型),来替代object对象类型,使用方法为Record<string, unknown>,<>中分别定义key,value的类型,例如<string, number>就表示object的key是string类型,number是value的类型。而一般使用<string, unknown>来表示常规的obejct类型。所以遇到函数中获取属性的,可以如下书写:

function getValue<T extends Record<string, unknown>, K extends keyof T> (obj: T, params: K) {
  return obj[params]
}

3.keyof和typeof的区别

在于keyof是获取类型成员的属性名,也就是说keyof不能用于变量,即

interface person {
  name: string
}
let obj = {
   name: 'liming'
}
keyof obj // 这种是错误的
keyof person // 这种是正确的
// 那么假如我只知道变量,不知道变量的类型,怎么获取属性
// 组合使用 :
type newObj = keyof typeof obj // 这样先将变量转化为类型,再使用keyof即可