在 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]
然而,启用严格模式后,最后一行会出现一个有趣的错误:
元素隐式具有“任意”类型,因为类型“{online:string;offline:string;busy:string;dnd:string;}”没有索引签名。
……如果你真的不知道发生了什么,这看起来完全是胡扯。所以,我们试着分解一下。
索引签名
在 TypeScript 中,为了获取对象的索引,该对象的类型必须包含索引签名。索引签名通常用于定义用作字典的对象,就像我们这里的例子一样。索引签名类型如下所示:
type Dictionary = { [index: string]: string }
我们的对象被推断为类型{ online: string; offline: string; busy: string; dnd: string; }
并且没有包含索引签名。
这样做的原因是为了防止使用未知键索引对象时出现运行时错误。假设我们正在使用的 API 添加了一个新的状态“idle”,当我们尝试使用它时,statusDisplays['idle']
就会返回undefined
错误。或者更糟的是,默默地失败。
一个直接的解决方案
为了在 JS 中解决这个问题,首先检查密钥是否有效就足够了,TypeScript 通常可以适应这些类型的检查。
if (userStatus in statusDisplays) {
statusDisplays[userStatus]
}
不幸的是,这仍然会产生相同的错误消息,原因在这里和其他类似问题中讨论过。
以下是使用辅助函数解决这个问题的一种方法:
// `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
}
if (hasKey(statusDisplays, userStatus)) {
statusDisplays[userStatus] // works fine!
}
这使用了一些你可能不知道的有趣特性。即泛型、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'
最后,我们在这里使用类型保护,表示如果此函数返回 true,则任何后续使用key
都将是指定类型。否则,它仍然只是一个字符串。
所有这些之所以有效,是因为 TypeScript 允许我们索引任何对象,只要索引的类型是所有可能键的并集,它就知道该键是有效的。也许将来 usingkey in obj
可以单独工作,但在此之前,辅助函数已经足够好了。
如果您有任何问题或意见,特别是如果我遗漏了什么或有任何不清楚的地方,请随时在下方留言。感谢您的阅读!
文章来源:https://dev.to/mapleleaf/indexing-objects-in-typescript-1cgi