我无法弄清楚索引签名和记录类型之间的区别。有人可以解释差异以及何时使用一种与另一种吗?
具体来说,我希望定义一个对象的类型,该对象的键和值将具有随机字符串,这些字符串将被迭代。
有没有区别:
let objectVariable: Record<string, string> = {}
和
let objectVariable2: {[index: string]: string} = {}
我无法弄清楚索引签名和记录类型之间的区别。有人可以解释差异以及何时使用一种与另一种吗?
具体来说,我希望定义一个对象的类型,该对象的键和值将具有随机字符串,这些字符串将被迭代。
有没有区别:
let objectVariable: Record<string, string> = {}
和
let objectVariable2: {[index: string]: string} = {}
的定义Record
是:
/**
* Construct a type with a set of properties K of type T
*/
type Record<K extends keyof any, T> = {
[P in K]: T;
};
创建类似 的类型时type MyType = Record<string, string>;
,内联会Record
导致以下类型:
type MyType = {
[P in string]: string;
};
这就是说在 set 中创建一个具有字符串属性名称的对象类型string
。因为是无限的,所以string
字符串的可能性是无限的(不像字符串文字类型的联合,比如."prop1" | "prop2"
string
所以是的,从类型检查的角度来看,它基本上等同于没有映射类型的索引签名的示例 ( { [index: string]: string; }
.
使用普通索引签名
以Record
这种方式使用有点奇怪,很多人可能不明白发生了什么。当可以有任意数量的属性时,表达意图的一种更常见的方法是不使用映射类型:
type ObjectWithStringProperties = {
[index: string]: string;
};
这具有帮助解释密钥应该是什么的额外好处。例如:
type PersonsByName = {
[name: string]: Person;
};
const collection: PersonsByName = {};
请注意,通过这种方式,类型是不同的,因为使用具有这种类型的对象的开发人员将在他们的编辑器中查看这些额外描述的键名信息。
使用Record
请注意,Record
通常使用如下:
type ThreeStringProps = Record<"prop1" | "prop2" | "prop3", string>;
// goes to...
type ThreeStringProps = { [P in "prop1" | "prop2" | "prop3"]: string; };
// goes to...
type ThreeStringProps = {
prop1: string;
prop2: string;
prop3: string;
};
使用而不是简单的索引签名是否是一个好主意Record
可能是一个有争议的问题(正如 David Shereet 在他的回答中指出的那样)。此外,你可以用一个简单的索引签名做更多的事情,Record
这也是应该提到的。
这个问题的主要部分(在我的阅读中)是这两种类型是否相同。它们显然以不同的方式声明,但它们是同一类型。虽然它们显然是兼容的(即您可以将一个分配给另一个,反之亦然),但问题是是否存在不可能的极端情况。
虽然很难找到一个类型的详尽列表,但 Matt McCutchen 在这个答案中提供了一个有趣的类型,可以检测修饰符存在的天气readonly
(简单的兼容性无法检测到两者之间的差异)。我推测如果Record
和索引签名在 Matt 在那里使用它们的方式中被认为是相同的(作为泛型函数签名的一部分),它们几乎是以不同的方式声明的相同类型:
type IfEquals<X, Y> =
(<T>() => T extends X ? 1 : 2) extends
(<T>() => T extends Y ? 1 : 2) ? "Y" : "N";
let same : IfEquals<{x: string}, {x: string}>= "Y"
let notsame : IfEquals<{ y: string }, { x: string }>= "N"
let notsamero: IfEquals<{ readonly x: string }, { x: string }> = "N"
let samerecord: IfEquals<{ [x: string]:string }, Record<string, string>> = "Y"
正如我们在最后一个示例中看到的,类型的samerecord
意思Y
是编译器将这两种类型视为同一事物。因此,我会推测{ [x: string]:string }
并且 Record<string, string>
完全相同。