考虑使用一个对象来翻译代码或进行国际化。
const statusMap = {
'A': 'Active',
'S': 'Suspended',
'D': 'Inactive',
}
只要显式地使用键'A' | 'S' | 'D'
(statusMap.A
),从这个对象获取值就可以正常工作,但是当处理未知字符串(statusMap[queryParams.status]
)时,我们会得到错误 “No index signature with a parameter of type 'string' was found on type”。这在纯JavaScript中有效,所以这是类型的问题。理想情况下,我希望statusMap[queryParams.status]
返回string | undefined
。
使用任何字符串访问对象值的最方便和最安全的方法是什么?
有没有可能在不创建接口的情况下向这样的对象添加索引器?
这里有一些我已经考虑过的解决方案,但它们都有缺点。
1.将map转换为索引类型
const statusMap: Record<string, string | undefined> = {
'A': 'Active',
'S': 'Suspended',
'D': 'Inactive',
}
statusMap[anyString]
这很好用,但是我们抛弃了自动完成(输入statusMap.
不会再为我们提供A、S或D的建议)。另一个小问题是,即使'A'
存在,statusMap.A
也会导致string | undefined
。
此外,很难强制要求每个团队成员都正确地进行选角。例如,您可以只强制转换为Record<string, string>
,而statusMap['nonexistent_key']
将导致string
类型,即使实际值是undefined
。
1.每次访问Map
(statusMap as Record<string, string | undefined>)[anyString]
这有相同的问题,确保每个团队成员都正确转换,需要在每次使用时重复。
1.每次访问时转换索引字符串
statusMap[anyString as keyof typeof statusMap]
这是一种丑陋的,也是不正确的-没有保证anyString实际上是'A' | 'S' | 'D'
。
1.在tsconfig中使用suppressImplicitAnyIndexErrors
方便,但不是类型安全的,这只是回避了这个问题,并且自TypeScript 5.0以来,该选项已被弃用。
1.效用函数
function getValSafe<T extends {}>(obj: T, key: string | number): T[keyof T] | undefined {
return (obj as any)[key]
}
getValSafe(statusMap, anyString)
这工作得很好,但我不喜欢将自定义实用程序函数推到每个项目中的想法,只是为了在TypeScript中执行基本操作。
有没有更好的办法呢?
类似问题:this one使用了一个接口,但我想避免这种情况,想象一下有一个巨大的i18 nMap,甚至是一棵树。This question使用了一个更复杂的转换函数,我只是想像在常规JavaScript中一样使用对象Map,并且仍然具有类型安全和自动完成功能。
3条答案
按热度按时间wtzytmuj1#
首先,我建议使用satisfies操作符来检查
statusMap
是否是Record<string, string>
:选项1:我们可以定义一个
Record<"A" | "S" | "D" | string, string>
类型的对象,但是,如果你尝试定义一个Record<string, string>
类型的对象,你会得到Record<string, string>
,因为"A" | "S" | "D"
是string
的一个子集,编译器会将键简化为string
。为了防止这种情况,我们可以添加一个交集,而不是只添加string
:string & {}
.{}
不会打扰我们,因为string
扩展了{}
,结果将是string
,尽管它会阻止简化,这正是我们所需要的:Playground
选项2:创建一个自定义类型保护来检查
statusMap
中是否有任何字符串:用途:
Playground
eqzww0vc2#
这些方法的问题在于,它们只是试图欺骗类型检查器接受不正确的值,并假装它们是正确的。这与创建严格类型检查的目的正好相反。(这是因为与Java不同,TS类型检查不会在运行时强制执行类型一致性。
因此,我们可以尝试在运行时实际检查
anyString
的值,对输入进行清理。理论上这正是
in
类型保护的目的,不幸的是,当属性名不是常量时,类型检查器还不能解决这个问题。所以你需要构建一个类型 predicate :
现在您可以执行以下操作:
或者更好的方法是,不再返回
undefined
,而是停止处理输入并显示错误消息。或者使用默认的"unknown status"
值。一旦你明确地处理了不正确的输入,选择权就掌握在你的手中。3zwtqj6y3#
使用
satisfies
: