通过关键对象获取动态类型

Get Dynamic Type by key object

我需要将枚举值传递给模板键,上下文键只显示在 TemplatesMail 键中引用的属性 但 TS 显示所有属性,如 CancellationPolicyContext 和 AnotherMailContext

enum TemplateEnum {
  CANCELLATION_POLICY = 'cancellation-policy',
  ANOTHER_MAIL = 'another-mail'
}

type CancellationPolicyContext = {
  name: string
  cancellationDate: Date
}

type AnotherMailContext = {
  value: number
}

type TemplatesMail = {
  [TemplateEnum.CANCELLATION_POLICY]: CancellationPolicyContext
  [TemplateEnum.ANOTHER_MAIL]: AnotherMailContext
}

type SendMailParams = {
  template: TemplateEnum
  context: TemplatesMail[TemplateEnum]
} 

const params: SendMailParams = {
  template: TemplateEnum.CANCELLATION_POLICY,
  context: {
    // need show only properties from CancellationPolicyContext
  }
}

类型问题

type SendMailParams = {
  template: TemplateEnum
  context: TemplatesMail[TemplateEnum]
} 

是它允许每个 属性 对应于 TemplateEnum 枚举 union 中的任何值。没有约束强制 templatecontext 属性分别对应于相同的枚举值。所以 {template: TemplateEnum.CANCELLATION_POLICY, context: AnotherMailContext} 类型的值是允许的。

你真正想要做的是说 对于 TemplateEnum 联盟中的每个 成员,你想要一个对应的 SendMailParams 类型.所以你想 分配 TemplateEnum 中的联合到原始 SendMailParams 类型。

为此,我们可以使用 分布式对象 类型(在 ms/TS#47109). It's a mapped type into which you immediately index 中创造,形式为 {[P in K]: F<P>}[K]。如果 KK1 | K2 | K3 | ... 形式的类键类型(例如 TemplateEnum)的并集,然后上面的分布式对象类型计算为 F<K1> | F<K2> | F<K3> | ....

这里是 SendMailParams:

type SendMailParams = { [K in TemplateEnum]: {
  template: K,
  context: TemplatesMail[K]
} }[TemplateEnum];

IntelliSense 告诉你这个类型是

/* type SendMailParams = {
    template: TemplateEnum.CANCELLATION_POLICY;
    context: CancellationPolicyContext;
} | {
    template: TemplateEnum.ANOTHER_MAIL;
    context: AnotherMailContext;
} */

这是你想要的联盟。事实上它是一个 discriminated union 所以编译器将强制 template 属性 决定对象其余部分的形状。现在,如果您编写 template: TemplateEnum.CANCELLATION_POLICY,类型检查器会立即将 context 属性 视为类型 CancellationPolicyContext,并且您的 IntelliSense 只会向您显示适当的属性:

const params: SendMailParams = {
  template: TemplateEnum.CANCELLATION_POLICY,
  context: {
    cancellationDate: new Date(),
    name: "abc"
  }
} // okay

const badParams: SendMailParams = {
  template: TemplateEnum.CANCELLATION_POLICY,
  context: {
    value: 1 // error!
  }
}

Playground link to code