首页 > 解决方案 > 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 中解决此问题的推荐方法是什么?

标签: typescript

解决方案


在运行时,您可以使用Proxy.

TypeScript 不能帮助强键入过去的键string,因为类型系统没有能力表示字符串操作对字符串文字类型的影响。因此,如果您想要一个具有keystring已知键的对象,编译器将不知道它KEYSTRING也是一个已知键。有一些关于操作字符串文字类型的建议,例如在它们上使用正则表达式,但目前没有任何内容是该语言的一部分。幸运的是,我认为,您要求的是字典类型,这通常意味着键是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));
      }
    }
  );
}

让我们使用它,传入一个将键转换为小写的规范化函数(如果它是 a 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?!大多数开发人员会非常惊讶地发现他们所谓的普通对象具有不区分大小写的键(一个(例如,KeyString)作为错误。如果您这样做是为了处理用户输入,那么只定义允许输入进入程序的严格障碍可能会更干净,并在那里规范化案例,因此字典可以完全不知道并被实现为普通对象.

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

链接到代码


推荐阅读