【问题标题】:Template literal containing keyof used as an interface key包含用作接口键的 keyof 的模板文字
【发布时间】:2021-11-12 12:35:52
【问题描述】:

给定一个简单的界面:

interface Schedule {
  interval: "month" | "day";
  price: number;
}

我可以构造一个模板文字类型(使用ts4.4):

type PrefixedScheduleKey = `schedule__${keyof Schedule}`; // "schedule__interval" | "schedule__price"

假设有一些这样的接口,我想合并(基本上展平)它们的前缀、模板文字键以结束:

interface Metadata {
  foo: string;
  bar: string;
  // computed 
  schedule__interval: "month" | "day";
  schedule__price: number;
  ...
}

我应该如何声明这样的接口键?

interface Metadata {
  [key: PrefixedScheduleKey]: any
}

// throws 
// An index signature parameter type cannot be a literal type or generic type. Consider using a mapped object type instead.(1337)

和

interface Metadata {
  [key in `schedule__${keyof Schedule}`]: any 
}

// leads to
// A computed property name must be of type 'string', 'number', 'symbol', or 'any'.(2464)
// Member '[key in `schedule__${keyof' implicitly has an 'any' type.(7008)

这是playground。

顺便说一句,使用type 而不是interface 是suggested on github,但这不起作用,因为我的Metadata 接口已经扩展了另一个接口,但我试图保持示例简单。

【问题讨论】:

  • interface Metadata extends AnotherInterface, Record<PrefixedScheduleKey, any> {} 怎么样?
  • 这行得通,谢谢@Jean-Alphonse!随意将其转换为答案。

标签: typescript template-literals


【解决方案1】:

这不可能直接在界面上执行,如下所述:Typescript: Mapped types with Interface

到目前为止,在 TypeScript 中(从 2.7.2 版开始),联合类型签名在 不允许使用接口,而是应使用映射类型(如 你正确)。

Docs.

但是,您可以使用任意接口和映射类型之间的交集类型as in the original GitHub issue you mentioned(“将其他成员移动到与交集类型组合的单独对象类型”)来实现这一点。您还可以使用as keyword 有效地内联您的PrefixedScheduleKey 模板文字类型,同时保留您的key 足够长的时间以引用Schedule[key]。没有理由不能对其他带前缀的类型执行相同的操作,无论是在相同的映射元数据类型中,还是在您以后相交的相邻元数据类型中。

interface BareMetadata /* extends ArbitraryType */ {
  foo: string;
  bar: string;
}

type ScheduleMetadata = {
  [key in keyof Schedule as `schedule__${key}`]: Schedule[key];
}

type MergedMetadata = BareMetadata & ScheduleMetadata;

const example: MergedMetadata = {
  foo: "one",
  bar: "two",
  schedule__interval: "month",
  schedule__price: 9.99
};

Playground Link

令我惊讶的是,you can even have the interface extend an object-like type,这可能会帮助您避免显式交集:

interface MergedMetadata extends BareMetadata, ScheduleMetadata {}
// or
interface Metadata extends ScheduleMetadata {
  foo: string;
  bar: string;
}

Playground Link

【讨论】:

  • 谢谢——效果很好!
  • @Joe 不客气。查看更新:在研究这个时,我也学到了一些新东西。 :)
  • 谢谢——我也学到了一些新东西。当我将其视为“联合”时,我发现他们称其为“交叉点”有点令人困惑。我希望多个不同接口的“严格交集”是never ?
  • @Joe-ElasticsearchHandbook 这个心智模型也花了我一段时间。它帮助我这样想:联合意味着“这个值可以是这些接口中的任何一个,因此您的交互必须仅与它们共同具有的属性”。交集是“此值必须匹配所有这些接口,因此您可以与任何贡献接口上指定的属性进行交互而无需检查”。
  • 很好的比喻@Jeff.
猜你喜欢
  • 2022-08-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-17
  • 1970-01-01
  • 1970-01-01
  • 2012-08-15
  • 2022-11-22
相关资源
最近更新 更多