编程全栈开发者

TypeScript中的keyof运算符是如何工作的,它的用途是什么,以及它如何影响安全功能和API的设计?

用 Hintsage AI 助手通过面试

答案。

问题背景

TypeScript从一开始就被开发用于对对象进行严格类型检查,并编写更具可预测性的JavaScript代码。为了确保在访问对象属性时的安全,出现了keyof运算符,它在TypeScript 2.1中引入。它允许创建表示在声明的对象类型中可用的字符串(和符号)键的集合的类型。

问题

在纯JavaScript中,对象的属性可以通过任何字符串进行查询,如果出错,结果将是undefined或错误,这只有在运行时才能发现。没有严格的类型检查,轻易会出现拼写错误或在重构时忘记更新键字符串。此外,通常需要构建一个仅接受特定对象或类型的正确键的函数。

解决方案

keyof运算符创建一个联合类型,包含类型的所有键。通过它,可以限制允许的键,提高API的安全性(例如,用于getter/setter函数),并创建通用的实用类型。

示例代码:

type User = { name: string; age: number; active: boolean }; type UserKey = keyof User; // "name" | "age" | "active" function getProp<T, K extends keyof T>(obj: T, key: K): T[K] { return obj[key]; } const user: User = { name: 'Tom', age: 33, active: true }; const age = getProp(user, 'age'); // age类型为number

关键特性:

  • 允许声明依赖于对象结构的“键”类型。
  • 使得类型为get/set属性的函数尽可能地类型安全——拼写错误和不存在的键被排除。
  • 从TypeScript 2.7及以上版本,支持符号属性(symbol)。

反向问题。

keyof对数组返回什么,是否可以获取索引?

对于数组,keyof返回的是数组在JS中实际作为对象的键类型。通常这是索引字符串和数组的特殊属性。

示例代码:

type A = keyof number[]; // "length" | "toString" | "pop" | ... | number

keyof可以返回union-types的键吗?

是的,keyof对于union类型返回两个对象键的交集,而不是它们的并集。

示例代码:

type A = {a: string, b: number } type B = {b: number, c: boolean } type C = keyof (A | B); // "b"

如果对象属性是可选的,会发生什么?keyof支持“?”吗?

是的,可选属性也包含在结果键类型中,但这并不意味着该属性在运行时必定存在于对象中。

示例代码:

type D = { a: string; b?: number }; type DK = keyof D; // "a" | "b"

类型错误和反模式

  • 使用常量字符串作为键而不是keyof——失去类型检查。
  • 认为keyof对union返回并集而不是交集。
  • 期望keyof只考虑显式声明的键,而不是继承的。

生活中的示例

负案例

编写函数getProperty(obj, key),使用字符串类型的键调用,不存在的键导致的错误——错误仅在运行时出现。

优点:

  • 通用代码。

缺点:

  • 编译阶段没有检查,重构时可能出现错误,容易出现拼写错误。

正案例

使用泛型:getProperty<T, K extends keyof T>(),密钥类型严格限制在结构的键。

优点:

  • 绝对的类型安全,拼写错误或键错误是不可能的。

缺点:

  • 代码稍微复杂,泛型对于初学者并不总是容易理解。