TS 中的 keyof 和 typeof 操作符

TS 中的 keyof 和 typeof 操作符

TypeScript keyof 操作符
使用object(key)获取对象属性时报错
Typescript - 类型操作符

keyof

keyof 用于遍历某种类型的属性(可以操作接口、类以及基本数据类型)
在 TS 中定义一个获取函数属性的函数如下:

function prop(obj: object, key: string) {
  return obj[key];
}

在上面代码中,为了避免调用 prop 函数时传入错误的参数类型,我们为 obj 和 key 参数设置了类型,分别为 {} 和 string 类型。然而,事情并没有那么简单。针对上述的代码,TypeScript 编译器会输出以下错误信息:

Element implicitly has an ‘any’ type because expression of type ‘string’ can’t be used to index type ‘{}’.

元素隐式地拥有 any 类型,因为 string 类型不能被用于索引 {} 类型。要解决这个问题,你可以使用以下非常暴力的方案:

function prop(obj: object, key: string) {
  return (obj as any)[key];
}

该函数用于获取某个对象中指定属性的属性值。因此我们期望用户输入的属性是对象上已存在的属性,那么如何限制属性名的范围呢?这时我们可以利用本文的主角 keyof 操作符:

function prop<T extends object, K extends keyof T>(obj: T, key: K) {
  return obj[key];
}

在以上代码中,我们使用了 TypeScript 的泛型和泛型约束。首先定义了 T 类型并使用 extends 关键字约束该类型必须是 object 类型的子类型,然后使用 keyof 操作符获取 T 类型的所有键,其返回类型是联合类型,最后利用 extends 关键字约束 K 类型必须为 keyof T 联合类型的子类型。

keyof 与对象的数值属性

在使用对象的数值属性时,我们也可以使用 keyof 关键字。请记住,如果我们定义一个带有数值属性的对象,那么我们既需要定义该属性,又需要使用数组语法访问该属性:

class ClassWithNumericProperty {
  [1]: string = "Semlinker";
}

let classWithNumeric = new ClassWithNumericProperty();
console.log(`${classWithNumeric[1]} `);

下面我们来举个示例,介绍一下在含有数值属性的对象中,如何使用 keyof 操作符来安全地访问对象的属性:

enum Currency {
  CNY = 6,
  EUR = 8,
  USD = 10
}

const CurrencyName = {
  [Currency.CNY]: "人民币",
  [Currency.EUR]: "欧元",
  [Currency.USD]: "美元"
};

console.log(`CurrencyName[Currency.CNY] = ${CurrencyName[Currency.CNY]}`);
console.log(`CurrencyName[36] = ${CurrencyName[6]}`);

为了方便用户能根据货币类型来获取对应的货币名称,我们来定义一个 getCurrencyName 函数,具体实现如下:

function getCurrencyName<T, K extends keyof T>(key: K, map: T): T[K] {
  return map[key];
}

console.log(`name = ${getCurrencyName(Currency.CNY, CurrencyName)}`);

同样,getCurrencyName 函数和前面介绍的 prop 函数一样,使用了泛型和泛型约束,从而来保证属性的安全访问。

typeof

typeof 操作符用于获取变量的类型。因此这个操作符的后面接的始终是一个变量,且需要运用到类型定义当中。为了方便大家理解,我们来举一个具体的示例:

type Person = {
  name: string;
  age: number;
}

let man: Person = {
  name: "Semlinker",
  age: 30
}

type Human = typeof man;

typeof 和 keyof 一起使用

const COLORS = {
  red: 'red',
  blue: 'blue'
}

// 首先通过typeof操作符获取color变量的类型,然后通过keyof操作符获取该类型的所有键,
// 即字符串字面量联合类型 'red' | 'blue'
type Colors = keyof typeof COLORS 
let color: Colors;
color = 'red' // Ok
color = 'blue' // Ok

// Type '"yellow"' is not assignable to type '"red" | "blue"'.
color = 'yellow' // Error

如果直接 keyof 一个对象变量的话,获取到的并不是该对象的属性组成的联合类型,而会是该对象的方法及属性的联合类型,比如除了属性还会有 “valueOf” “toString” 等。

思考

最后留到思考题,有兴趣的小伙伴可以想一想:

interface StringIndexArray {
  [index: string]: string;
}

interface NumberIndexArray {
  [index: number]: string;
}

type K1 = keyof StringIndexArray // type K1 = string | number
type K2 = keyof NumberIndexArray // type K2 = number

Keyof inferring string | number when key is only a string

你可能感兴趣的:(React,typescript)