如何根据字符串值检查泛型类型映射器是否具有键

how to check if generic type mapper has key according to a string value

我正在尝试弄清楚如何使用通用类型映射器和字符串值: 我有一个名为 getItemsPerKey(itemsMapper : Map, distinctIdsList : number[]) 的函数。K 类型是 数字或字符串。我想检查 stringnumber 的 K 类型值是否相等(或应用 has(key) 扩展函数)到一个特定的字符串值。

这是相关代码:

export class SomeService
{
    //currently K can be number or string
    getItemsPerKey<K, V>(itemsMapper : Map<K,V[]>, distinctIdsList : number[]) 
    {
        let filteredItemsMapper : Map<K, V[]> = new Map();
        for (let [key, itemsTypeV] of Object.entries(itemsMapper))  
        {
            if (distinctIdsList.includes(+key))
            {
                /*This is problematic - "Argument of type 'string' is not assignable to parameter of type 'K'.
  'K' could be instantiated with an arbitrary type which could be unrelated to 'string'."*/ 
                if (filteredItemsMapper.has(key))
                {
                    ....
                }
            }
        }
    }
}

真正的问题是您使用的是 Object.entries() on itemsMapper, which is not at all going to do what you want. You are confusing the keys/values held by the map as data, verses the keys/values on the itemsMapper object itself. You should instead be using Map.prototype.entries()。也就是说:

  • 不要Object.entries(itemsMapper)
  • DO 写入 itemsMapper.entries()

为了演示,我们做一个Map<string, number>来看看:

const testMap = new Map([["a", 1], ["b", 2]]);

其中有两个条目。但是如果我们使用 Object.entries(),什么也不会发生:

for (let [key, val] of Object.entries(testMap)) {
  console.log(key, val) // nothing happens
}

console.log(Object.entries(testMap).length); // 0

嗯,它有 没有 个条目?我以为它有两个。问题是 Object.entries() 而不是 遍历地图保存的数据;它遍历地图的 own enumerable 属性。其中有none。我们可以添加一些:

const mapWithAdditionalProp = Object.assign(
  new Map([["a", 1], ["b", 2]]), 
  { someProperty: "hello" }
);
console.log(mapWithAdditionalProp.someProperty.toUpperCase()); // HELLO

现在 mapWithAdditionalProp 是一个包含这两个条目的映射,但它也有自己的 属性,名为 someProperty。现在让我们做 Object.entries():

for (let [key, val] of Object.entries(mapWithAdditionalProp)) {
  console.log(key, val) // "someProperty", "hello"
}

所以,如果我们试图将一些其他属性推到地图对象本身,那就太好了。但是,如果您试图真正获取地图中的数据,那就太糟糕了。

另一方面,Map.prototype.entries() 正是为了遍历地图保存的数据:

for (let [key, val] of testMap.entries()) {
  console.log(key, val) // "a", 1 ; "b", 2
}

所以你应该使用 itemsMapper.entries() 而不是 Object.entries(itemsMapper)。当你这样做时:

class SomeService {
  //currently K can be number or string <-- then you should constrain it to be such
  getItemsPerKey<K extends string | number, V>(
    itemsMapper: Map<K, V[]>, 
    distinctIdsList: number[]
  ) {
    let filteredItemsMapper: Map<K, V[]> = new Map();
    for (let [key, itemsTypeV] of itemsMapper.entries()) {
      if (distinctIdsList.includes(+key)) {
        if (filteredItemsMapper.has(key)) {
        }
      }
    }
  }
}

编译没有错误。万岁!更好的是,它可能会在运行时执行您想要的操作。


请注意,stringK 问题间接地告诉了您问题所在。 JavaScript 中的对象只有 string(或 symbol)键。 Object.entries() 只给你 string 键。所以 Object.entries(itemsMapper) 将给你一个数组对,其中每对中的第一个项目是 string。你得到 Array<[string, any]>.

但是 Map<K, V> 可以是任何类型,如 K;它可以使用 strings 或 numbers 或 Dates 作为键(尽管在 Map 中使用对象作为键通常不是人们想要做的,因为它们被比较引用)。当您调用 itemsMapper.entries() 时,您会得到 IterableIterator<[K, V[]]>(它不是一个数组,但您可以像一个数组一样对其进行迭代),并且每对中的第一项是 K,如您所愿。

所以修复不是用has()强制检查string键;而是检查正确的密钥类型。

Playground link to code