【问题标题】:What is the Record type in typescript?打字稿中的记录类型是什么?
【发布时间】:2019-01-26 21:58:10
【问题描述】:

Record<K, T> 在 Typescript 中是什么意思?

Typescript 2.1 引入了Record 类型,并举例说明:

// For every properties K of type T, transform it to U
function mapObject<K extends string, T, U>(obj: Record<K, T>, f: (x: T) => U): Record<K, U>

Typescript 2.1

Advanced Types 页面在 ReadonlyPartialPick 旁边的映射类型标题下提到了 Record,似乎是它的定义:

type Record<K extends string, T> = {
    [P in K]: T;
}

Readonly、Partial 和 Pick 是同态的,而 Record 不是。 Record 不是同态的一个线索是它不需要输入类型来复制属性:

type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>

就是这样。除了以上引用,typescriptlang.org 上没有其他提及Record

问题

  1. 有人可以简单定义Record 是什么吗?

  2. Record&lt;K,T&gt; 仅仅是一种说法“这个对象上的所有属性都将具有T 类型”吗?可能不是所有属性,因为K 有一些目的......

  3. K 泛型是否禁止对象上不是K 的附加键,还是允许它们并仅指示它们的属性未转换为T

  4. 用给定的例子:

    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
    

    和这个一模一样吗?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
    

【问题讨论】:

  • 4. 的答案几乎是“是”,所以这应该可以回答您的其他问题。

标签: typescript typescript2.0


【解决方案1】:
  1. 有人可以简单定义Record 是什么吗?

Record&lt;K, T&gt; 是一种对象类型,其属性键为 K,其属性值为 T。也就是说,keyof Record&lt;K, T&gt; 等价于KRecord&lt;K, T&gt;[K] (基本上)等价于T

  1. Record&lt;K,T&gt; 是否只是一种说法“此对象上的所有属性都将具有T 类型”?可能不是所有的对象,因为K 有一些目的......

正如您所注意到的,K 有一个目的......将属性键限制为特定值。如果您想接受所有可能的字符串值键,您可以执行类似Record&lt;string, T&gt; 的操作,但惯用的方法是使用index signature 之类的{ [k: string]: T }

  1. 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
  1. 用给定的例子:

    type ThreeStringProps = Record<'prop1' | 'prop2' | 'prop3', string>
    

    和这个一模一样吗?:

    type ThreeStringProps = {prop1: string, prop2: string, prop3: string}
    

是的!

希望对您有所帮助。祝你好运!

【讨论】:

  • 学到了很多,还有一个问题,为什么“这样做的惯用方法是使用索引签名”而不是记录?我没有找到关于这种“惯用方式”的任何相关信息。
  • 如果你愿意,你可以使用Record&lt;string, V&gt;来表示{[x: string]: V};我什至可能自己也做过。索引签名版本更直接:它们是相同的类型,但前者是映射类型的类型别名,其计算结果为索引签名,而后者只是直接的索引签名。在其他条件相同的情况下,我推荐后者。同样,我不会使用Record&lt;"a", string&gt; 代替{a: string},除非有其他令人信服的上下文原因。
  • "在其他条件相同的情况下,我推荐后者。" 为什么?我的预 Typescript 自己同意,但我知道前者会更多,嗯,对于来自 C# 方面的人来说,自我评论,例如,对于 JavaScript 到 Typescripters 来说也不会更糟。您是否只是对跳过这些构造的转译步骤感兴趣?
  • 只是我的看法:Record&lt;string, V&gt; 的行为只有在你已经知道索引签名在 TypeScript 中是如何工作的情况下才有意义。例如,给定x: Record&lt;string, string&gt;x.foo 在编译时显然是string,但实际上很可能是string | undefined。这是--strictNullChecks 工作方式的一个差距(请参阅#13778)。我宁愿让新来者直接处理{[x: string]: V},而不是期望他们遵循从Record&lt;string, V&gt;{[P in string]: V} 到索引签名行为的链。
  • Record可以用作C#字典吗? Records 有 add()、remove() 方法吗?
【解决方案2】:

记录允许您从联合创建新类型。 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 生活在您未定义的类型中?否则我可以看到像一个更适合枚举的接口之类的东西,对吧?
  • 嗨@victorio,我不确定枚举如何解决问题,如果你错过了一个键,你不会在枚举中得到错误。它只是键和值之间的映射。
  • 我明白你现在的意思了。来自 C#,我们没有聪明的方法来做到这一点。最接近的是Dictionary&lt;enum, additional_metadata&gt; 的字典。 Record 类型是表示枚举 + 元数据模式的好方法。
  • 有没有办法初始化一个有多个键映射到一个值的记录?假设我有 10 种不同的状态,但在 UI 中,它们都应该显示一个警告图标。如果我使用记录类型,我将不得不重复相同的 10 次,如果我使用 switch 语句之类的东西,我可以避免这种情况。
猜你喜欢
  • 2016-11-06
  • 1970-01-01
  • 1970-01-01
  • 2022-01-19
  • 1970-01-01
  • 2021-03-14
  • 2019-03-30
  • 2019-01-10
  • 1970-01-01
相关资源
最近更新 更多