简体   繁体   中英

how to use TypeScript union types properly?

I'm having trouble with basic union types, appreciate some help! Building a chat app where incoming messages can have a payload of a varying type. So I created a few payload types like:

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

bound them all up in a union type just to be clear, using |

export type MessagePayload = TextPayload | ImagePayload | UrlPayload | FilePayload

and then the final Message uses that as the payload.

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

but when trying to use I get this error

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

The code is used here. Possibly its the destructuring assignment is confusing the type system...

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

another line throwing error

    let text = msgIn.payload.text

Full error

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

it's almost as if the compiler just bailed halfway on ImagePayload...

Does a union type mean properties have to exist on every member, not just one? The subtypes have to be a superset of the interface? I don't quite see the point in that case. I did also try with just TextPayload , eg not a union type and got a similar error... confused.

I'm also a little confused between types and interfaces. Why isn't this a UnionInterface ?

Thanks for any tips.

MS reference https://www.typescriptlang.org/docs/handbook/advanced-types.html

full code

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
}

Here's why TypeScript is throwing an error - it's trying to do good actually, but coding things right can be tricky (trust me, I've been there , and also 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

Check out this code live in the TS playground

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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