简体   繁体   English

Typescript通用接口在属性值上匹配

[英]Typescript generic interfaces match on property value

I am still learning TypeScript and all the features it has. 我仍在学习TypeScript及其所有功能。 One of which is constraining generics. 其中之一是约束泛型。 I apologize if this is a commonly asked question, if you have any resources(beyond the docs) that can help me get better at this, please link as comment. 抱歉,这是一个常见问题,如果您有任何资源(超出文档范围)可以帮助我对此有所改善,请链接以评论。

What I'm trying to do is, have the type properties match between my DeliveryObject and all the objects inside of deliveryItems . 我想做的是,让我的DeliveryObjectdeliveryItems内部的所有对象之间的type属性匹配。

Here's an example of code that compiles, but just isn't the end solution that I'm looking for. 这是一个可编译的代码示例,但这不是我想要的最终解决方案。

type DeliveryMethod = 'notification' | 'text' | 'email'

type DeliveryActions = INotificationAction | ITextMessageAction | IEmailAction

interface IDelivery {
  type: DeliveryMethod
}

interface INotificationAction extends IDelivery {
  type: 'notification'
  deviceId: string
  message: string
}

interface ITextMessageAction extends IDelivery {
  type: 'text'
  message: string
}

interface IEmailAction extends IDelivery {
  type: 'email'
  to: string
  subject: string
  body: string
}

// I know I need to do something additional here...
interface IDeliveryObject<T extends DeliveryMethod, U extends DeliveryActions> {
  type: T
  deliveryItems: Array<U>
}

function sendDelivery<K extends DeliveryMethod, Z extends DeliveryActions>(state: IDeliveryObject<K, Z>) {
  console.log(state)
}

sendDelivery({
  type: 'notification', // <--- needs to match or error out
  deliveryItems: [
    {
      type: 'email',  // <--- needs to match or error out
      to: 'fake@email.com',
      subject: '1235-67890',
      body: 'Here is a notification'
    }
  ]
})

I would approach this by using a "type lookup map" to tie together the delivery method and it's associated action object. 我将通过使用“类型查找图”将传递方法及其关联的操作对象绑定在一起来解决此问题。 So I would add another type like this: 所以我要添加另一种这样的类型:

type DeliveryActionTypes = {
    "notification": INotificationAction;
    "text": ITextMessageAction;
    "email": IEmailAction;
}

That type just maps the correct method name to it's action object type. 该类型只是将正确的方法名称映射到其操作对象类型。 Then you can replace the declarations for DeliveryMethod and DeliveryActions with: 然后,您可以将DeliveryMethodDeliveryActions的声明替换为:

type DeliveryMethod = keyof DeliveryActionTypes;
type DeliveryActions = DeliveryActionTypes[keyof DeliveryActionTypes];

That will allow you to easily lookup the correct action if you know the name of the method. 如果您知道该方法的名称,则可以轻松查找正确的操作。 You can use that in your IDeliveryObject to make sure that the two types correspond: 您可以在IDeliveryObject使用它来确保两种类型相对应:

interface IDeliveryObject<T extends DeliveryMethod> {
    type: T;

    // This is the type lookup, note the `DeliveryActionTypes[T]` part.
    deliveryItems: Array<DeliveryActionTypes[T]>;
}

Now you can simplify the signature for the sendDelivery function, since all it needs now is the method name: 现在,您可以简化sendDelivery函数的签名,因为它现在所需要的只是方法名称:

function sendDelivery<K extends DeliveryMethod>(state: IDeliveryObject<K>) {
  console.log(state)
}

With all that, you'll get an error if the types don't match: 所有这些,如果类型不匹配,则会出现错误:

sendDelivery({
  type: 'notification',
  deliveryItems: [
    {
      type: 'email', 
      to: 'fake@email.com', // <-- error on this line, see below
      subject: '1235-67890',
      body: 'Here is a notification'
    }
  ]
})
 Type '{ type: "email"; to: string; subject: string; body: string; }' is not assignable to type 'INotificationAction'. 

As you can see, Typescript correctly concludes that the items in the array should be of type INotificationAction , and produces an error when they are not. 正如你所看到的,打字稿正确的结论是,数组中的项目应该是类型INotificationAction ,而当它们不产生错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM