如何正确使用TypeScript联合类型?

时间:2019-07-03 18:33:31

标签: node.js typescript types union-types

我在使用基本联合类型时遇到麻烦,请多多帮助! 构建一个聊天应用程序,其中传入的消息可以具有不同类型的有效负载。所以我创建了一些有效载荷类型,例如:

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
}

1 个答案:

答案 0 :(得分:1)

这就是TypeScript引发错误的原因-它实际上试图做的很好,但是正确编写代码可能很棘手(请相信我,I've been therealso there):

// 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

实时查看此代码in the TS playground