在 TypeScript 中索引对象

2025-06-07

在 TypeScript 中索引对象

这是一个时常出现的话题,所以我想写一篇关于它的文章会很有用。

假设你有如下代码:

// an object defining how to display specific user statuses in the UI
const statusDisplays = {
  online: 'Online',
  offline: 'Offline',
  busy: 'Busy',
  dnd: 'Do Not Disturb',
}

// fetch the status of a user by their ID
const userStatus = getUserStatus(myUserID)

// get the displayed status text
const displayedStatus = statusDisplays[userStatus]
Enter fullscreen mode Exit fullscreen mode

然而,启用严格模式后,最后一行会出现一个有趣的错误:

元素隐式具有“任意”类型,因为类型“{online:string;offline:string;busy:string;dnd:string;}”没有索引签名。

……如果你真的不知道发生了什么,这看起来完全是胡扯。所以,我们试着分解一下。

索引签名

在 TypeScript 中,为了获取对象的索引,该对象的类型必须包含索引签名。索引签名通常用于定义用作字典的对象,就像我们这里的例子一样。索引签名类型如下所示:

type Dictionary = { [index: string]: string }
Enter fullscreen mode Exit fullscreen mode

我们的对象被推断为类型{ online: string; offline: string; busy: string; dnd: string; }并且没有包含索引签名。

这样做的原因是为了防止使用未知键索引对象时出现运行时错误。假设我们正在使用的 API 添加了一个新的状态“idle”,当我们尝试使用它时,statusDisplays['idle']就会返回undefined错误。或者更糟的是,默默地失败。

一个直接的解决方案

为了在 JS 中解决这个问题,首先检查密钥是否有效就足够了,TypeScript 通常可以适应这些类型的检查。

if (userStatus in statusDisplays) {
  statusDisplays[userStatus]
}
Enter fullscreen mode Exit fullscreen mode

不幸的是,这仍然会产生相同的错误消息,原因在这里和其他类似问题中讨论过

以下是使用辅助函数解决这个问题的一种方法:

// `PropertyKey` is short for "string | number | symbol"
// since an object key can be any of those types, our key can too
// in TS 3.0+, putting just "string" raises an error
function hasKey<O>(obj: O, key: PropertyKey): key is keyof O {
  return key in obj
}
Enter fullscreen mode Exit fullscreen mode
if (hasKey(statusDisplays, userStatus)) {
  statusDisplays[userStatus] // works fine!
}
Enter fullscreen mode Exit fullscreen mode

这使用了一些你可能不知道的有趣特性。即泛型keyof用户定义的类型保护

我们使用泛型来推断传入对象的形状,以便在返回类型中使用它。在这里,O的参数hasKey被推断为{ online: string; offline: string; busy: string; dnd: string; }

keyof是 TypeScript 中的一个关键字,它接受给定的对象类型并返回其键的联合类型。它们等效于:

type StatusKey = keyof { online: string; offline: string; busy: string; dnd: string; }
type StatusKey = 'online' | 'offline' | 'busy' | 'dnd'
Enter fullscreen mode Exit fullscreen mode

最后,我们在这里使用类型保护,表示如果此函数返回 true,则任何后续使用key都将是指定类型。否则,它仍然只是一个字符串。

所有这些之所以有效,是因为 TypeScript 允许我们索引任何对象,只要索引的类型是所有可能键的并集,它就知道该键是有效的。也许将来 usingkey in obj可以单独工作,但在此之前,辅助函数已经足够好了。

如果您有任何问题或意见,特别是如果我遗漏了什么或有任何不清楚的地方,请随时在下方留言。感谢您的阅读!

文章来源:https://dev.to/mapleleaf/indexing-objects-in-typescript-1cgi
PREV
Dockerfile 创建自定义容器的基础知识
NEXT
使用 Angular Minesweeper 开发的经典扫雷游戏