Родовые интерфейсы Typescript совпадают по значению свойства - PullRequest
0 голосов
/ 08 июня 2018

Я все еще изучаю TypeScript и все его возможности.Одним из которых является ограничение дженериков.Я прошу прощения, если это часто задаваемый вопрос, если у вас есть какие-либо ресурсы (помимо документов), которые могут помочь мне в этом помочь, пожалуйста, ссылку в качестве комментария.

Я пытаюсь сделать так, чтобы свойства type совпадали между моим DeliveryObject и всеми объектами внутри deliveryItems.

Вот пример кода, который компилируется, но просто не является конечным решением, которое я ищу.

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'
    }
  ]
})

1 Ответ

0 голосов
/ 08 июня 2018

Я бы подошел к этому, используя «карту поиска типов», чтобы связать воедино метод доставки и связанный с ним объект действия.Поэтому я бы добавил еще один тип, подобный этому:

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

Этот тип просто отображает правильное имя метода в тип объекта действия.Затем вы можете заменить объявления для DeliveryMethod и DeliveryActions на:

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

Это позволит вам легко найти правильное действие, если вы знаете имя метода.Вы можете использовать это в вашем IDeliveryObject, чтобы убедиться, что эти два типа соответствуют:

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

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

Теперь вы можете упростить сигнатуру для функции sendDelivery, так как теперь все, что ей нужно, это имя метода:

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

При всем этом вы получите ошибку, если типы не совпадают:

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'.

Как выКак видите, Typescript правильно заключает, что элементы в массиве должны иметь тип INotificationAction, и выдает ошибку, когда их нет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...