这篇文章介绍了TypeScript中强类型字典的不同方法。字典有时被称为哈希或地图--基本上它是一个键值对的集合。在这篇文章中,我们将关注键值未知的字典--如果我们知道键值,那么可以使用一个类型别名或接口。

问题是
TypeScript需要在访问对象之前了解其完整表示。例如,下面的代码在JavaScript中是正常的,但在TypeScript中会引发类型错误:
let scores = {};
scores.bill = 10; // 💥 - Property 'bill' does not exist on type '{}'
下面的代码在JavaScript中向控制台输出undefined ,但在TypeScript中引发了类型错误:
let scores = { bill: 10 };
console.log(scores.fred); // 💥 - Property 'fred' does not exist on type '{ bill: number; }'
我们可以在一个类型注解中使用any ,但是这样就不会对字典进行类型检查:
let scores: any = {};
scores.bill = 10; // ✔️ - no type error
scores.invalidProp = true; // ✔️ - no type error
我们希望发生一些类型检查,但又能灵活地在运行时向字典中添加键。
使用索引的对象类型注解
我们可以使用一个索引对象类型注解,如下所示:
let scores: { [name: string]: number } = {};
scores.bill = 10; // ✔️ - no type error
scores.bill = "10"; // 💥 - Type 'string' is not assignable to type 'number'
这里我们指定字典的键是字符串,值是数字。
"name "标签可以是我们喜欢的任何东西。通常使用 "key":
let scores: { [key: string]: number } = {};
scores.bill = 10;
不过标签不能省略:
let scores: { [string]: number } = {};
// 💥 - 'string' only refers to a type, but is being used as a value here
不幸的是,我们不能用联合类型来限制键:
let scores: {
[name: "bill" | "bob"]: number;
} = {};
// 💥 - An index signature parameter type cannot be a union type. Consider using a mapped object type instead
从一个更积极的角度来看,我们可以有更复杂的值类型:
type Person = {
email: string;
rating: number;
};
let scores: { [name: string]: Person } = {};
scores.bill = {
email: "bill@somewhere.com",
rating: 9,
};
scores.bob = {
emailAddress: "bill@somewhere.com",
// 💥 Type '{ emailAddress: string; rating: number; }' is not assignable to type 'Person'.
rating: 9,
};
使用Record 实用类型
有一个Record 实用类型,它比索引对象类型更简洁一些。它也允许键的类型是一个联合类型:
let scores: Record<string, number> = {};
scores.bill = 10; // ✔️ - no type error
scores.trevor = "10"; // 💥 - Type 'string' is not assignable to type 'number'
我们可以使用联合类型来缩小键的类型,如下所示。
let scores: Record<"bill" | "bob", number> = {};
scores.bill = 10; // ✔️ - no type error
scores.trevor = 10; // 💥 - Property 'trevor' does not exist on type 'Record<"bill" | "bob", number>'
使用Map
Map是一个标准的JavaScript功能,对于保存键值对非常有用。
对于Map ,有一个相应的TypeScript类型,叫做Map 。这是一个通用类型,它接收键和值的类型作为参数:
let scores = new Map<string, number>();
scores.set("bill", 10);
scores.set("bob", "10"); // 💥 - Argument of type 'string' is not assignable to parameter of type 'number'.
我们可以对键使用联合类型,对值使用对象类型,如下所示。
type Person = {
email: string;
rating: number;
};
let scores = new Map<"bill" | "bob", Person>();
scores.set("bill", {
email: "bill@somewhere.com",
rating: 9,
});
Map 的一个好处是,它提供了一个很好的API来访问对象中的项目:
let scores = new Map<"bill" | "bob", Person>();
scores.set("bill", {
email: "bill@somewhere.com",
rating: 9,
});
scores.set("bob", {
email: "bob@somewhere.com",
rating: 9,
});
console.log(scores.has("bill")); // true
scores.forEach((person) => console.log(person));
// { "email": "bill@somewhere.com", "rating": 9 }
// { "email": "bob@somewhere.com", "rating": 9 }
很好!
总结
Record 实用类型是一种简明的方法,可以对字典进行强类型化。如果我们想要一个围绕字典的更好的 API,我们可以使用Map 。