说我有以下几种类型:

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

然后,我消耗一条消息,如下所示:
[{name: 'example1'}, {name: 'potato'}].find(thing => thing.name === message.labelName)

导致以下流异常:
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.

type Message = MessageHead & MessageBody显示为违规类型

我不明白的是为什么我的联合体类型不允许带有标签名的消息?

编辑:Tryflow链接:Tryflow link

最佳答案

您的联合类型确实允许使用带有标签名的Message。问题在于它还允许不带标签名称的Message。考虑一下您在这条线上得到的联合:

type MessageBody = BaseBody | LabelledBody

这意味着MessageBody可以如下所示:
type BaseBody = {
  +payload?: any,
  +data?: any
}

或者
type LabelledBody = {
  +labelName: string
}

更进一步,我们可以执行MessageBody和MessageHead之间的交集,并看到Message的形状可以是以下两种情况之一:

(情况1)
{
  +type: MessageType,  // From MessageHead
  +payload?: any,      // From BaseBody
  +data?: any          // From BaseBody
}

(情况2)
{
  +type: MessageType,  // From MessageHead
  +labelName: string   // From LabelledBody
}

因此,当Flow看到您正在访问message对象的labelName时,它(正确地)认为message对象可能看起来像情况1(上述)。如果是第一种情况,那么您将无法访问labelName,因为它不存在,因此会引发错误。解决此问题的最简单方法是创建两种类型的消息,一种带有labelName,另一种带有payloaddata属性。然后,您可以在接收以下类型之一时对函数进行注释:

(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
  }
}

关于javascript - 联合体类型缺少属性,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/49351349/

10-16 21:15
查看更多