как правильно использовать типы объединения TypeScript?

У меня проблемы с основными типами профсоюзов, благодарю за помощь! Создание приложения чата, в котором входящие сообщения могут иметь полезную нагрузку различного типа. Итак, я создал несколько типов полезной нагрузки, например:

export interface TextPayload {
  text: string,
}
export interface ImagePayload {
  url: string,
} //etc

связал их все в тип объединения, чтобы быть ясным, используя |

export type MessagePayload = TextPayload | ImagePayload | UrlPayload | FilePayload

а затем последнее сообщение использует это как полезную нагрузку.

export interface IBotMsg {
  payload: MessagePayload  // this creates the problem
}

но при попытке использовать я получаю эту ошибку

[0]       TS2459: Type 'MessagePayload' has no property 'text' and no string index signature.

Здесь используется код. Возможно, его назначение деструктуризации сбивает систему типов с толку ...

  const msg: IBotMsg = req.body.msg
  const { payload: { text } } = msg

ошибка выдачи другой строки

    let text = msgIn.payload.text

Полная ошибка

[0]
[0] ERROR in ./server/bots/watson/routes/index.ts
[0] [tsl] ERROR in /Users/dc/dev/tix/recobot/stack/backend/server/bots/watson/routes/index.ts(19,22)
[0]       TS2459: Type 'MessagePayload' has no property 'text' and no string index signature.
[0]
[0] ERROR in ./server/bots/watson/routes/index.ts
[0] [tsl] ERROR in /Users/dc/dev/tix/recobot/stack/backend/server/bots/watson/routes/index.ts(40,25)
[0]       TS2339: Property 'text' does not exist on type 'MessagePayload'.
[0]   Property 'text' does not exist on type 'ImagePayload'.
[0]
[0] ERROR in ./server/bots/tix/brain/TixBrain.ts
[0] [tsl] ERROR in /Users/dc/dev/tix/recobot/stack/backend/server/bots/tix/brain/TixBrain.ts(27,30)
[0]       TS2339: Property 'text' does not exist on type 'MessagePayload'.
[0]   Property 'text' does not exist on type 'ImagePayload'.
[0]
[0] ERROR in /Users/dc/dev/tix/recobot/stack/backend/server/bots/testbot/TestBot.ts
[0] [tsl] ERROR in /Users/dc/dev/tix/recobot/stack/backend/server/bots/testbot/TestBot.ts(12,39)
[0]       TS2339: Property 'text' does not exist on type 'MessagePayload'.
[0]   Property 'text' does not exist on type 'ImagePayload'.

это почти как если бы компилятор только что отказался от ImagePayload ...

Означает ли тип объединения свойства должны существовать для каждого члена, а не только для одного? Подтипы должны быть надмножеством интерфейса? Я не совсем понимаю, в чем дело. Я также пробовал использовать только TextPayload, например, не тип объединения, и получил аналогичную ошибку ... запутался.

Я также немного запутался между типами и интерфейсами. Почему это не UnionInterface?

Спасибо за любые советы.

Справочник MS https://www.typescriptlang.org/docs/handbook/advanced-types.html

полный код

export enum MessageType {
  TEXT = 0,
  IMAGE = 1,
  URL_LINK = 2,
  FILE = 3,
}

export interface TextPayload {
  text: string,
  mention?: string[],
}

export interface ImagePayload {
  url: string,
}

export interface UrlPayload {
  sourceUrl: string,
  title: string,
  summary: string,
  imageUrl: string,
}

export interface FilePayload {
  url: string,
  name: string,
}

export type MessagePayload = TextPayload | ImagePayload | UrlPayload | FilePayload

export interface IBotMsg {
  chatId?: string,
  token?: string,
  messageType?: MessageType
  payload: MessagePayload  // this creates the problem
}

person dcsan    schedule 03.07.2019    source источник
comment
Does a union type mean properties have to exist on every member, not just one? - нет, для этого нужны типы пересечений   -  person artem    schedule 03.07.2019
comment
I'm also a little confused between types and interfaces. Why isn't this a UnionInterface ? - если вам нужен интерфейс, обладающий всеми свойствами ряда других интерфейсов, просто объявите интерфейс, расширяющий их все. Объединение и пересечение - это операции, определенные для типов, и они производят типы, а не интерфейсы. Интерфейс - это один из конкретных типов типов, существует множество других типов, которые не могут быть выражены как интерфейсы.   -  person artem    schedule 03.07.2019
comment
спасибо, но на самом деле мне нужен гибкий тип payload, который может иметь или не иметь член .text в зависимости от messageType. Так разве это невозможно с профсоюзами?   -  person dcsan    schedule 03.07.2019
comment
Возможно, вам просто нужно убедиться, что полученный вами объект действительно имеет свойство, к которому вы хотите получить доступ - обычно это делается с помощью размеченные объединения и оператор switch или if, который проверяет свойство kind, которое присутствует во всех типах членов union   -  person artem    schedule 03.07.2019
comment
В вашем случае компилятор не может знать, что фактический тип MessagePayload зависит от значения messageType - вы можете использовать утверждение типа или защиту настраиваемого типа, чтобы обойти это, но поместив messageType в полезную нагрузку и сделав MessagePayload дискриминируемым типом объединения было бы проще ИМО.   -  person artem    schedule 03.07.2019
comment
ты заставляешь это звучать просто! Хорошо, сейчас это any, и я раскрою это позже. Спасибо! Если вы хотите ответить, я могу принять его позже ..   -  person dcsan    schedule 03.07.2019


Ответы (1)


Вот почему TypeScript выдает ошибку - на самом деле он пытается приносить пользу, но правильное кодирование может быть непростым (поверьте мне, Я был там и также там):

// so let's say we have:
const msg: IBotMsg = ...

// if we write
msg.payload.text // it fails because msg could be equal to {payload:{url:'...'}}
msg.payload.url  // it fails because msg could be equal to {payload:{text:'...'}}
// so TypeScript prevents that and throws an error.

// You have to make clear to TypeScript that you know which properties you expect, either using typegards:
if ('text' in msg.payload) {
    msg.payload.text;
}

// ... or by casting:
const msg2: {payload: TextPayload} = (msg as {payload: TextPayload});
msg2.payload.text; // no error
msg2.payload.url;  // rightful error

Проверьте этот код жить в TS площадка

person Nino Filiu    schedule 03.07.2019
comment
вау спасибо, думаю теперь понял, поиграю с ней и проверю! спасибо за хорошие комментарии ex. - person dcsan; 04.07.2019