我有一个对象exampleMap
,它实现了DictionaryNum<number>
,作为键值存储。我使用一个对象,因为地图不适用于例如JSON.stringify
.
interface DictionaryNum<T> {
[id: number]: T;
}
let exampleMap = {
[1]: 1,
}
有时我必须迭代整个地图,例如在函数createQsFromDict()
中,并使用地图的键和值进行进一步处理。在这种情况下,我忽略了这一点,以保持示例简单。 但是,我使用Object.entries(dict).forEach()
遍历地图,因为它似乎是此类任务的最佳匹配(顺序无关紧要)。
function createQsFromDict(dict: DictionaryNum<number>): boolean {
Object.entries(dict).forEach(
([qP, aP]: number[]) => { //[qP = "1", aP = 1] "1"!=1
if (qP === 1) {
return true
}
})
return false
}
let works = createQsFromDict(exampleMap)
问题是,尽管我使用了两次类型注释(函数参数,lambda)并且没有产生错误,但在转译时,键是一个字符串,尽管TypeScript认为它是一个数字。因此,当我在构造函数中使用它时,对象将在对象中以字符串结束。 显然,这不是我想要的。我希望隐式转换或错误。
我的问题是: 获得所需行为的最佳方法是什么*。 目前我使用parseInt(qP)
并删除类型注释,但这似乎不是最佳解决方案,因为这正是我不想用 JavaScript 编码的原因。
*仍:
- 快速访问各个值
- 键/值的快速迭代
- JSON.stringifyable
Object.entries(obj)
生成一个[string, any]
数组(或者在obj
是T
数组或值均为T
的字符串键字典的情况下,可能[string, T]
合适的T
)。 这是因为,即使对于数组,JavaScript 实际上也会将所有索引(symbol
个索引除外)转换为字符串。 因此,{"1": "hey"}
和{1: "hey"}
是相同的值并且具有相同的类型。 TypeScript 公开number
索引是为了方便处理数组和类数组对象,但从技术上讲,这是一个谎言。 事实是,数组键属于像numericString
这样的类型,显然对任何人都无法处理太复杂了。
无论如何,您的问题是:为什么编译器不进行隐式转换或在forEach()
回调中抛出错误?
答:TypeScript 不做隐式转换,这将违反 TypeScript 非目标 #5 关于根据类型信息发出不同的 JS 代码......类型系统在运行时之前被完全擦除。 您只需要自己将字符串转换为数字,或者使用其他一些数据结构来表示字典(例如[number, number]
对数组或Map<number, number>
)
至于抛出错误,这是合理的。 但是,不幸的是,any
类型(又名类型系统的"逃生舱口")正在抑制这一点。 您正在获取一个预期为[string, any]
类型的值,并将其视为number[]
。 TypeScript 很乐意将像[A, B]
这样的元组类型视为Array<A|B>
。 但[string, any]
可以像Array<string | any>
一样对待,这是any[]
,也可以像number[]
一样对待而毫无怨言。 这对你来说是不幸的。
若要重新获得某种类型安全性,可以通过类型断言使用其他重载Object.entries(dict)
:
Object.entries(dict as Record<string, number>)
这将导致Object.entries()
返回一个Array<[string, number]>
,并且您的代码将出错,除非您实际上将密钥视为string
。 脱节的是标准库不希望有人将非数组类数字索引的东西传递给Object.entries()
。 如果您使用数组(具有length
属性的东西),它也会捕获错误。
所以这是一系列相当不幸的边缘情况,咬了你。 不好意思。 无论如何,希望有帮助。 祝你好运!
查看Object.entries
声明:
entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];
看起来entries
方法默认将对象属性转换为string
,您必须在回调中自行转换。
使用==
代替比较器===
是您的解决方案吗?
"1" === 1 // false
"1" == 1 // true