Object.entries(qs).forEach(...)-解析lambda忽略类型注释

时间:2019-05-12 13:24:37

标签: typescript

我有一个对象exampleMap,该对象实现了DictionaryNum<number>,用作键值存储。我使用一个对象,因为Map不适用于例如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

2 个答案:

答案 0 :(得分:2)

Object.entries(obj)生成[string, any]的数组(或者在[string, T]是{{1}的数组的情况下,可能生成T来生成合适的obj }或所有值为T的字符串字典。这是因为,即使对于数组,JavaScript实际上也会将所有索引(T索引除外)都转换为字符串。因此symbol{"1": "hey"}是相同的值,并且具有相同的类型。 TypeScript公开了{1: "hey"}索引是为了方便处理数组和类似数组的对象,但是从技术上讲这是一个谎言。事实是,数组键的类型类似于number,显然任何人都可以使用too complicated

无论如何,您的问题是:编译器为什么不进行隐式转换或在numericString回调中引发错误?

答案:TypeScript不会进行隐式转换,这会违反TypeScript Non-Goal #5,它基于类型信息发出不同的JS代码...类型系统在运行时之前已被完全擦除。您只需要自己将字符串转换为数字,或使用其他数据结构来表示字典(例如,forEach()对数组或[number, number]

对于引发错误,这是您可以预期的。但是,不幸的是,Map<number, number>类型(也就是类型系统的“转义阴影”)正在抑制这种情况。您正在使用预期为any类型的值,并将其视为[string, any]。 TypeScript很乐意将number[]之类的元组类型视为[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()属性的数组),则也会捕获该错误。

因此,这是一系列非常不幸的边缘案例,使您难受。抱歉。无论如何,希望能有所帮助。祝你好运!

答案 1 :(得分:0)

查看Object.entries声明:

entries<T>(o: { [s: string]: T } | ArrayLike<T>): [string, T][];

看起来像entries方法默认将对象属性转换为string,而您必须在回调中自行转换。

使用==而不是===比较器为您提供解决方案吗?

"1" === 1 // false
"1" == 1 // true