说我有以下类型:
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
答案 0 :(得分:2)
您的联合类型 允许带有标签名的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(上图)。如果我们在案例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
}
}