Missing properties of a FlowJS object

Let's say I have the following types:

type MessageType = 'example1' | 'example2' | 'example3'

type MessageHead = {
  +type: MessageType
}

type BaseBody = {
  +payload?: any,
  +data?: any
}

type LabelledBody = {
  +labelName: string
}

type MessageBody = BaseBody | LabelledBody

type Message = MessageHead & MessageBody

And then I use a message like this:

[{name: 'example1'}, {name: 'potato'}].find(thing => thing.name === message.labelName)

The result is the following stream exception:

Cannot get message.labelName because:
 • all branches are incompatible:
    • Either property labelName is missing in MessageHead [1].
    • Or property labelName is missing in BaseBody [2].
 • ... 1 more error.

C is type Message = MessageHead & MessageBodydisplayed as a broken type

What I do not understand, why my connection type does not allow messages with a label name?

Edit: tryflow link : tryflow link

+4
source share
1 answer

The union type allows you to use Messagelabels with the name. The problem is that it also allows Messageuntagged labels. Consider the union that you have on this line:

type MessageBody = BaseBody | LabelledBody

This means it MessageBodymight look like this:

type BaseBody = {
  +payload?: any,
  +data?: any
}

or

type LabelledBody = {
  +labelName: string
}

, MessageBody MessageHead , :

( 1)

{
  +type: MessageType,  // From MessageHead
  +payload?: any,      // From BaseBody
  +data?: any          // From BaseBody
}

( 2)

{
  +type: MessageType,  // From MessageHead
  +labelName: string   // From LabelledBody
}

, Flow , Message, (), Message 1 (. ). 1, labelName, , . - : labelName payload data . :

(Try)

type MessageWithLabel = MessageHead & LabelledBody

const exFunc = (message: MessageWithLabel) => {
  [{name: 'example1'}, {name: 'potato'}].find(thing => thing.name === message.labelName)
}

disjoint union, , . (, type), , .

(Try)

type MessageWithBody = {|
  +type: 'base',
  +payload?: any,
  +data?: any
|}

type MessageWithLabel = {|
  +type: 'labelled',
  +labelName: string
|}

type Message = MessageWithBody | MessageWithLabel

const exFunc = (message: Message) => {
  if (message.type === 'labelled') {
    const labelName = message.labelName
    return [{name: 'example1'}, {name: 'potato'}].find(thing => thing.name === labelName)

    // Sidenote: 
    // I had to extract labelName to a constant for Flow to typecheck this correctly.
    // So if we used thing.name === message.labelName Flow will throw an Error.
    // If you don't extract it right away, flow thinks the Message may have changed
    // after pretty much any non-trivial action. Obviously it doesn't change in this
    // example, but, hey, Flow isn't perfect.
    // Reference: https://flow.org/en/docs/lang/refinements/#toc-refinement-invalidations
  } else {
     // Do something for the 'base' type of message
  }
}
0

Source: https://habr.com/ru/post/1695025/


All Articles