Record<K, T>
在 Typescript 中是什么意思?
Typescript 2.1 引入了 Record
类型,并在示例中对其进行了描述:
// 对于 T 类型的每个属性 K,将其转换为 U function mapObject
Advanced Types 页面在 Readonly
、Partial
和 Pick
旁边的映射类型标题下提到了 Record
,其定义似乎如下:
type Record
Readonly、Partial 和 Pick 是同态的,而 Record 不是。 Record 不是同态的一个线索是它不需要输入类型来复制属性: type ThreeStringProps = Record<'prop1' | '道具2' | 'prop3',字符串>
就是这样。除了上述引文外,typescriptlang.org 上没有其他提及 Record
。
问题
有人可以简单定义一下 Record 是什么吗? Record
有人可以简单定义一下 Record 是什么吗?
Record<K, T>
是一种对象类型,其属性键为 K
,其属性值为 T
。也就是说,keyof Record<K, T>
等价于 K
,而 Record<K, T>[K]
(基本上)等价于 T
。
Record
正如您所注意到的,K
有一个目的......将属性键限制为特定值。如果您想接受所有可能的字符串值键,您可以执行 Record<string, T>
之类的操作,但这样做的惯用方法是使用 { [k: string]: T }
之类的 index signature。
K 泛型是否禁止对象上不是 K 的附加键,或者它是否允许它们并仅指示它们的属性未转换为 T?
它并没有完全“禁止”附加键:毕竟,通常允许一个值具有未在其类型中明确提及的属性......但它不会识别出这样的属性存在:
declare const x: Record<"a", string>;
x.b; // error, Property 'b' does not exist on type 'Record<"a", string>'
它会将它们视为 excess properties ,有时会被拒绝:
declare function acceptR(x: Record<"a", string>): void;
acceptR({a: "hey", b: "you"}); // error, Object literal may only specify known properties
有时接受:
const y = {a: "hey", b: "you"};
acceptR(y); // okay
对于给定的示例: type ThreeStringProps = Record<'prop1' | '道具2' | 'prop3', string> 是不是和这个一模一样?:type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
是的!
希望有帮助。祝你好运!
Record
允许您从联合创建新类型。 Union 中的值用作新类型的属性。
例如,假设我有一个这样的联盟:
type CatNames = "miffy" | "boris" | "mordred";
现在我想创建一个包含所有猫信息的对象,我可以使用 CatNames
联合中的值作为键创建一个新类型。
type CatList = Record<CatNames, {age: number}>
如果我想满足这个 CatList
,我必须创建一个像这样的对象:
const cats: CatList = {
miffy: { age:99 },
boris: { age:16 },
mordred: { age:600 }
}
你会得到非常强大的类型安全性:
如果我忘记了一只猫,我会得到一个错误。
如果我添加了不允许的猫,我会收到错误消息。
如果我稍后更改 CatNames,我会收到错误消息。这特别有用,因为 CatNames 可能是从另一个文件导入的,并且可能在许多地方使用。
真实世界的 React 示例。
我最近使用它来创建一个 Status
组件。该组件将接收一个 status
道具,然后呈现一个图标。出于说明目的,我在这里简化了很多代码
我有一个这样的工会:
type Statuses = "failed" | "complete";
我用它来创建一个像这样的对象:
const icons: Record<
Statuses,
{ iconType: IconTypes; iconColor: IconColors }
> = {
failed: {
iconType: "warning",
iconColor: "red"
},
complete: {
iconType: "check",
iconColor: "green"
};
然后我可以通过将对象中的元素解构为道具来进行渲染,如下所示:
const Status = ({status}) => <Icon {...icons[status]} />
如果以后扩展或更改 Statuses
联合,我知道我的 Status 组件将无法编译,我会收到一个可以立即修复的错误。这允许我向应用程序添加额外的错误状态。
请注意,实际的应用程序有几十个错误状态,这些错误状态在多个地方被引用,因此这种类型安全性非常有用。
type Statuses
生活在您未定义的类型中?否则我可以看到像一个更适合枚举的接口之类的东西,对吧?
Dictionary<enum, additional_metadata>
的字典。 Record 类型是表示枚举 + 元数据模式的好方法。
const bla: CatNames = "miffy";
有效。 TS 将 |
描述为联合类型,因此只需要 一个,但记录需要全部。非常混乱。 JS 和 TS 还是一团糟。
Record<string, V>
来表示{[x: string]: V}
;我什至可能自己也做过。索引签名版本更直接:它们是相同的类型,但前者是映射类型的类型别名,其计算结果为索引签名,而后者只是直接索引签名。在其他条件相同的情况下,我推荐后者。同样,除非有其他令人信服的上下文原因,否则我不会使用Record<"a", string>
代替{a: string}
。Record<string, V>
的行为只有在您已经知道索引签名在 TypeScript 中是如何工作的情况下才有意义。例如,给定x: Record<string, string>
,x.foo
在编译时显然是string
,但实际上很可能是string | undefined
。这是--strictNullChecks
工作方式的一个差距(参见 #13778)。我宁愿让新手直接处理{[x: string]: V}
,而不是期望他们遵循从Record<string, V>
到{[P in string]: V}
的链到索引签名行为。