首页 > 解决方案 > 包含用作接口键的 keyof 的模板文字

问题描述

给定一个简单的界面:

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)

这里是游乐场

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

标签: typescripttemplate-literals

解决方案


这不可能直接在接口上执行,如此处所述:Typescript: Mapped types with Interface

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

文档

但是,您可以使用任意接口和映射类型之间的交集类型来实现这一点,如您提到的原始 GitHub 问题(“将其他成员移动到与交集类型组合的单独对象类型”)。您还可以使用as关键字有效地内联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
};

游乐场链接

令我惊讶的是,您甚至可以使用interface类似对象的扩展type,这可能会帮助您避免显式交集:

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

游乐场链接


推荐阅读