对象和记录是我们在类型级 TypeScript 中可操作的两种最为常见的数据结构。它们将成为我们的类型算法之基石,因此了解它们的工作原理是至关重要的。我相信您已经使用过对象和记录,但是我希望本章能够助您发展对于它们所表示的值的直觉,并向您展示它们藏在冰山一角下的真面貌。
type SomeObject = { key1: boolean; key2: number };
type SomeRecord = { [key: string]: number };
在我们开始之前,让我提醒您我们在前一章里讨论过的一些重要概念,因为它们也与本章相关。在深入研究类型与集合论之间的关系之前,我们简要地讨论了类型的五个主要类别ーー原始值、字面量、数据结构、联合和交叉。
我们发现类型表示值的集合。与集合一样,类型也可以包括其他类型,我们称这种关系为子类型化。
子类型层次结构
最后,我们认识了两种特殊的类型:never
ーー空集和 unknown
ーー包含其它一切的集合。
现在,让我们将目光集中到前一章中所提及的四种数据结构类型中的两种:对象和记录:
type FourKindsOfDataStructures =
| { key1: boolean; key2: number } // objects
| { [key: string]: number } // records
| [boolean, number] // tuples
| number[]; // arrays
对象类型定义 JavaScript 对象集。创建对象类型的语法与我们创建常规对象的方式非常相似:
type User = {
name: string;
age: number;
role: "admin" | "standard";
};
事实上,它们是 JS 对象在类型级别中的等价物。和 JS 对象一样,它们可以包含任意多的属性,并且每个属性都由一个唯一的键进行索引。请注意,每个键可以包含不同的类型:name
键包含类型为 string
的值,但是 age
键在这里包含类型为 number
的值。
我们创建的 User
类型是包含一个 name
、一个 age
和一个 role
属性的所有对象的集合,这些对象包含正确类型的值:
// ✅ this object is in the `User` set.
const gabriel: User = {
name: "Gabriel",
role: "admin",
age: 28,
};
// ❌
const bob: User = {
name: "Bob",
age: 45,
// <- the `role` key is missing.
};
// ❌
const peter: User = {
name: "Peter",
role: "standard",
age: "45" /* <- the `age` key should be of type `number`,
but it's assigned to a `string`. */,
};
但是那些具有额外属性的对象呢? 它们是否也可以分配给我们的 User
类型?
好吧,是的...也不是,听我解释!
如果您尝试像我们目前所做的那样,使用显式类型注释将一个对象分配给一个变量,TypeScript 将拒绝额外的属性: