TypeScript中不区分大小写的字典

时间:2019-08-28 12:57:24

标签: typescript

在C#中,可以使用StringComparer创建不区分大小写的字典(对于字符串类型的键):

Dictionary<string, int> dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);

我正在寻找TypeScript中的相同功能,但是在网上搜索时,看起来好像它尚未内置在TypeScript中。

作为一种解决方法,我目前在代码中的任何地方使用toLowerCase(),向字典添加值或从字典中读取值。但是,这样容易出错,我正在寻找更好的解决方案。

示例: 添加值后

dict = {};
dict['keystring'] = 1;

我希望能够使用dict['keystring]dict['KEYSTRING']来访问它,而不必担心正确的大小写。建议使用TypeScript解决此问题的方法是什么?

2 个答案:

答案 0 :(得分:2)

在运行时,您可以非常接近使用Proxy的要求。

TypeScript在强行键入string之后的键方面无济于事,因为类型系统无法表示对string literal types的字符串操作的影响。因此,如果您希望使用keystring作为已知键的对象,则编译器将不知道KEYSTRING也是已知键。对于处理字符串文字类型(例如using regular expressions on them),有一些建议,但是目前该语言中没有任何内容。幸运的是,我想您要的是字典类型,这通常意味着键只是string。只要您不希望编译器为您跟踪有效的密钥,就可以了。

这是使用Proxy的可能实现。请注意,我还没有完全测试所有可能的边缘情况,但是基本思想是使所有接受属性键的事物都成为障碍,并将其替换为规范版本:

function dictWithCanonicalKeys<V>(canon: (prop: keyof any) => keyof any) {
  return new Proxy<{ [k: string]: V }>(
    {},
    {
      get(target, prop) {
        return Reflect.get(target, canon(prop));
      },
      set(target, prop, value) {
        return Reflect.set(target, canon(prop), value);
      },
      has(target, prop) {
        return Reflect.has(target, canon(prop));
      },
      defineProperty(target, prop, attribs) {
        return Reflect.defineProperty(target, canon(prop), attribs);
      },
      deleteProperty(target, prop) {
        return Reflect.deleteProperty(target, canon(prop));
      },
      getOwnPropertyDescriptor(target, prop) {
        return Reflect.getOwnPropertyDescriptor(target, canon(prop));
      }
    }
  );
}

让我们使用它,传入一个规范化函数,该函数将密钥转换为小写字母(如果它是string,即...它只剩下symbol个密钥):

const dict = dictWithCanonicalKeys<number | undefined>(
  p => (typeof p === "string" ? p.toLowerCase() : p)
);

dict.keyString = 1;
console.log(dict.KEYSTRING); // 1
dict.KEYstring = "two"; // error! "two" is not a number
dict.KEYstring = 2; 
console.log(dict.keyStrinG); // 2
console.log(JSON.stringify(dict)); // {"keystring": 2};
console.log("KeyString" in dict); // true
Object.keys(dict).forEach(k => console.log(k + ":" + dict[k])); // keystring:2

在我看来,这一切都很合理。


最后,请注意:您应该小心地在代码中引入类似的内容。对于大多数开发人员来说,发现他们所谓的普通对象具有不区分大小写的键(console.log(Object.keys(dict).indexOf("keyString")); // -1?!)实在令人惊讶,即使您可以向开发人员解释它,其IDE也无法理解,并且可能会标记一个与众不同的键如果来自预期的错误(例如KeyString)是错误的。如果您这样做是为了处理用户输入,则可以在允许输入程序的地方定义严格的障碍,然后规范该情况,这样可能会更干净一些,因此该词典可能很不为人所知,并且可以将其实现为普通对象。

当然,只有您知道用例,不区分大小写的类字典对象才是正确的选择。如果是这样,希望以上代码对您有所帮助。祝你好运!

Link to code

答案 1 :(得分:0)

这是不可能的。无论如何,具有动态添加属性的普通JS对象并不是真正的通用字典。 JS中的等效项是Map,但密钥也does not support replacing the comparison operator

您的方法可能是最好的选择。您可以通过将存储空间(您的普通JS对象或Map)包装在包装对象中来对其进行形式化,以确保使用键的所有操作在进行比较或查找之前都将它们变为小写。