Mar*_*sse 10 types record typescript
考虑以下代码:
const testOne: Record<"foo"|"bar", string> = {
"foo": "xyz"
};
const testTwo: Record<string, string> = {
"foo": "xyz"
};
Run Code Online (Sandbox Code Playgroud)
第一个示例会导致错误,指出缺少属性“bar”。第二个示例不会导致错误。这让我感到困惑,因为我试图了解 Record 是否是一种暗示其键类型的所有可能值的现有属性的类型。
如果 Record 是一种不要求所有可能的键实际存在于该类型的值中的类型,那么第一个示例不应导致错误。
如果 Record是一种要求所有可能的键实际存在于该类型的值中的类型,那么第二个示例也应该会导致错误。在这种情况下,不可能构造该类型的值,因为可能的键集是无限的。
如果有第三种选择——这似乎是根据我尝试编译示例时实际发生的情况而定的——它是什么?我发现两种键类型之间的主要区别在于,一种具有有限的值集,另一种具有无限的值集。这是用来区分的吗?
除此之外,我能找到的唯一解释是 Record 不仅根据其键类型的值集进行区分,还根据其键类型的其他一些属性进行区分。如果是这样,密钥类型的哪些属性会产生影响?或者 Record 是否执行相当于“绕过接口,转换为实现类型并执行一些您不应该执行的操作”的类型系统?
Record的实现是
type Record<K extends keyof any, T> = {
[P in K]: T;
};
Run Code Online (Sandbox Code Playgroud)
我在这里可以发现两件事。第一个是将 K 绑定到“keyof any”,但据我所知,这限制了 K 可使用哪些类型,而不是限制了结果类型的有效值。其次,我们有一个正常的索引签名,所以我的猜测是,我在 Record 中感到困惑的实际上是索引签名的行为 - 但由于其他问题,我无法在没有 Record 的情况下轻松重现此行为,所以我不这样做不想急于下结论。
我们先从实现开始:
type Record<K extends keyof any, T> = {
[P in K]: T;
};
Run Code Online (Sandbox Code Playgroud)
keyof any- 仅表示可用作任何对象的键的所有允许类型。现在,出于这样的目的,您可以使用PropertyKey内置类型。
{[P in K]: T;}这只是一个常规for..in循环。
因此,当您将联合类型"foo"|"bar"作为第一个参数传递给 时Record,TS 编译器只会迭代每个联合并创建如下内容:
type Result = {
foo:string,
bar: string,
}
Run Code Online (Sandbox Code Playgroud)
这意味着您的最终对象应该具有最小属性集:foo和bar。
但是,当你只是string作为第一个参数传递时,情况就会有所不同。
type Result = Record<string, string>
type Result2 = {
[P in string]: string
}
Run Code Online (Sandbox Code Playgroud)
正如您可能已经注意到的,Result和Result2是相同的类型。
现在,您可能认为这Record<string, string>等于索引接口:
interface Indexed {
[prop: string]: string
}
type Result = Record<string, string>
type Check = Result extends Indexed ? true : false // true
type Check2 = Indexed extends Result ? true : false // true
Run Code Online (Sandbox Code Playgroud)
但这些类型的行为有点不同。看这个答案
更新
问题似乎仍然是 Record 是否要求所有可能的属性都实际存在。如果 Record 不要求所有可能的属性实际存在,为什么结果是具有必填字段而不是可选字段的对象类型?
请参阅映射类型文档
来自文档:
映射类型建立在索引签名的语法之上,索引签名用于声明尚未提前声明的属性的类型:
因此,键入Record<string, string>, 意味着您不确切知道将使用哪些键来记录,但您 100% 确定它将是string. 这是设计使然。
为什么Partial<Record<string, string>>与 不一样Record<string, string>,因为意味着 value也Partial可以。undefined
换句话说,Partial<Record<string, string>>等于Record<string,string | undefined>
它如何适用于“无限”类型(例如字符串)
这意味着如果key是stringtype,你可以使用任何字符串来完成这个要求。不存在无限的字符串集。