【问题标题】:TypeScript Objects as Dictionary types as in C#TypeScript 对象作为 C# 中的字典类型
【发布时间】:2012-11-17 20:49:09
【问题描述】:

我有一些使用对象作为字典的 JavaScript 代码;例如,“人”对象将保存一些与电子邮件地址相关的个人详细信息。

var people = {<email> : <'some personal data'>};

adding   > "people[<email>] = <data>;" 
getting  > "var data = people[<email>];" 
deleting > "delete people[<email>];"

是否可以在 Typescript 中进行描述?还是我必须使用数组?

【问题讨论】:

  • 旧帖但注意有ES6 Map

标签: arrays object dictionary typescript


【解决方案1】:

在较新版本的打字稿中,您可以使用:

type Customers = Record<string, Customer>

在旧版本中您可以使用:

var map: { [email: string]: Customer; } = { };
map['foo@gmail.com'] = new Customer(); // OK
map[14] = new Customer(); // Not OK, 14 is not a string
map['bar@hotmail.com'] = 'x'; // Not OK, 'x' is not a customer

如果您不想每次都输入整个类型注释,您也可以创建一个接口:

interface StringToCustomerMap {
    [email: string]: Customer;
}

var map: StringToCustomerMap = { };
// Equivalent to first line of above

【讨论】:

  • 这是确保编译器将索引限制为字符串的有用方法。有趣的。看起来您不能将索引类型指定为字符串或整数以外的任何内容,但这是有道理的,因为它只是映射到本机 JS 对象索引。
  • 您可能知道这一点,但这种方法也有一些潜在的问题,最大的问题是没有安全且简单的方法来遍历所有成员。例如,此代码显示 map 包含两个成员: (Object.prototype).something = function(){};类客户{} var map: { [email: string]: Customer; } = { };地图['foo@gmail.com'] = 新客户(); for (var i in map){ console.log(map[i]) }
  • 如何从中删除?
  • 另一个有趣的方法是: interface MapStringTo { [key:string]:T;并且声明变量像var map:MapStringTo&lt;Customer&gt; = {};
  • 请注意,索引约束不再起作用。 Read more.
【解决方案2】:

您可以为此使用Record

https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkt

示例(AppointmentStatus 枚举和一些元数据之间的映射):

  const iconMapping: Record<AppointmentStatus, Icon> = {
    [AppointmentStatus.Failed]: { Name: 'calendar times', Color: 'red' },
    [AppointmentStatus.Canceled]: { Name: 'calendar times outline', Color: 'red' },
    [AppointmentStatus.Confirmed]: { Name: 'calendar check outline', Color: 'green' },
    [AppointmentStatus.Requested]: { Name: 'calendar alternate outline', Color: 'orange' },
    [AppointmentStatus.None]: { Name: 'calendar outline', Color: 'blue' }
  }

现在以接口为值:

interface Icon { Name: string Color: string }

用法:

const icon: SemanticIcon = iconMapping[appointment.Status]

【讨论】:

  • 这非常有用。你会使用字符串enum 还是class/object 来表示AppointmentStatus - 或者这有关系吗?
  • @Drenai 没关系,你喜欢什么
  • 我自己想通了,在找到这个答案之前在记录中使用枚举。主要好处是它强制进行详尽检查。意味着不能跳过枚举中的任何键。可以添加未包含在枚举中的任何键。这是一本带有运行类型检查的字典
【解决方案3】:

你也可以在 typescript 中使用 Record 类型:

export interface nameInterface { 
    propName : Record<string, otherComplexInterface> 
}

【讨论】:

    【解决方案4】:

    Lodash 有一个简单的 Dictionary 实现并且有很好的 TypeScript 支持

    安装 Lodash:

    npm install lodash @types/lodash --save
    

    导入和使用:

    import { Dictionary } from "lodash";
    let properties : Dictionary<string> = {
        "key": "value"        
    }
    console.log(properties["key"])
    

    【讨论】:

      【解决方案5】:

      除了使用 map-like 对象外,现在还有一个实际的Map object,在编译到 ES6 或使用 polyfill 时,它在 TypeScript 中可用ES6type-definitions:

      let people = new Map<string, Person>();
      

      它支持与Object 相同的功能,但语法略有不同:

      // Adding an item (a key-value pair):
      people.set("John", { firstName: "John", lastName: "Doe" });
      
      // Checking for the presence of a key:
      people.has("John"); // true
      
      // Retrieving a value by a key:
      people.get("John").lastName; // "Doe"
      
      // Deleting an item by a key:
      people.delete("John");
      

      仅此一项就比使用类似地图的对象有几个优点,例如:

      • 支持非基于字符串的键,例如数字或对象,Object 都不支持(不,Object 不支持数字,它将它们转换为字符串)
      • 不使用--noImplicitAny 时出错的空间更小,因为Map 始终具有key 类型和value 类型,而对象可能 没有索引签名
      • 添加/删除项目(键值对)的功能针对任务进行了优化,unlike creating properties on an Object

      此外,Map 对象为常见任务提供了更强大和更优雅的 API,其中大多数通过简单的Objects 无法使用,除非将帮助函数组合在一起(尽管其中一些需要完整的 ES6 迭代器/可迭代用于 ES5 或以下目标的 polyfill):

      // Iterate over Map entries:
      people.forEach((person, key) => ...);
      
      // Clear the Map:
      people.clear();
      
      // Get Map size:
      people.size;
      
      // Extract keys into array (in insertion order):
      let keys = Array.from(people.keys());
      
      // Extract values into array (in insertion order):
      let values = Array.from(people.values());
      

      【讨论】:

      • 太棒了!但遗憾的是,它使用JSON.stringify() 序列化时出错了,因此可以使用它,例如对于socket.io :(
      • @Lion -- 是的,Map 序列化相当有趣。我,一方面,在序列化之前执行到键值对对象的转换,然后返回(例如{ key: "John", value: { firstName: "John" } }的对象)。
      • 我犯了一个错误,使用地图而不是普通的旧对象,序列化真的让我着迷。在我看来要避开。
      • 这很漂亮。很高兴你启发了我最终涉足地图。这将几乎取代我通常的键盘映射/字典结构,因为强键入键要容易得多。
      • 是的,地图并不总是最好的选择,比如你想得到一个密钥但不区分大小写。不可能。
      【解决方案6】:

      你可以像这样使用模板化接口:

      interface Map<T> {
          [K: string]: T;
      }
      
      let dict: Map<number> = {};
      dict["one"] = 1;
      

      【讨论】:

      • 请注意,这与 es6 Map 类型冲突。比其他答案更好,因为索引约束被忽略了。
      • 如何检查字典中是否存在键?
      • dict.hasOwnProperty('key')
      • 我使用 Dictionary 而不是 Map 以避免混淆,您可以使用文字对象表示法:let dict: Dictionary&lt;number&gt; = { "one": 1, "two": 2 };
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-14
      • 2019-10-12
      • 2019-10-14
      • 2011-06-21
      • 2023-01-10
      • 1970-01-01
      • 2023-03-04
      相关资源
      最近更新 更多