Disjoint Unions do not work when using exact object types as defined
https://flow.org/en/docs/types/unions/#toc-disjoint-unions-with-exact-types
/* @flow */
type Foo = {|
+foo: 'foo',
|}
type Bar = {|
+foo: 'bar'
|}
type Baz = {|
+payload: string
|}
function example(obj: Foo | Bar | Baz) {
if (typeof obj.payload !== 'string') {
(obj: Foo | Bar)
}
}
in this case it errors because obj could be Baz... but there is actually ZERO possibility in this case it could possibly be Baz. In fact it would appear that it is 100% impossible to refine this object even with it being as strict as can be.
function example(obj: Foo | Bar | Baz) {
if (obj.foo === 'foo' || obj.payload === 'bar') {
(obj: Foo | Bar)
}
}
also does not work . here
nor does trying to take it a step further
function example<+O: Foo | Bar | Baz>(obj: O) {
if (typeof obj.payload !== 'string') {
(obj: Foo | Bar)
}
}
Union types work with string literals on one property that all members have in common. You reference "Disjoint unions with exact types", but then you go ahead and show an example that has a "member" (for Flow it isn't) that has a different property and even property type. So that isn't a union as described in the linked documentation. What remains is that yes, Flow has lots of holes. That's normal and expected and won't ever change. Those type systems on top of Javascript are a kludge and won't ever fill 100% of the available coding space, you really have to adjust your code and coding style and limit yourself to what actually works. Here: If you want to work with a disjoint union then don't try to mix it with something completely different.
It actually specifically mentions that if you want to compare different objects it’s fine if you use exact types. Also this is absolutdly something that can easily be inferred.
“With exact object types, we cannot have additional properties, so the objects conflict with one another and we are able to distinguish which is which.”
https://flow.org/en/docs/types/unions/#toc-disjoint-unions-with-exact-types
Sorry but given the example and the language I’m still not seeing why this wouldn’t work - on top of the fact that the type clearly defines a clear and concise and guaranteed method to differentiate them.
That entire section should be removed or should be better written to define when this won’t work.
It doesn't work because it isn't implemented, like 10,000+ other things that _should_ work. As I said... by mixing the disjoint union with something different you leave the space covered by the current Flow implementation. As I said, there are many, many many such limitations. It's certainly okay to write issues, just don't expect anything. The incompleteness (of Flow) is a given. Just use the parts that work. Like, disjoint unions (without mixing them with something else, unless you are lucky and it works).
Well then it should be documented that it doesn’t work or removed from the docs as it clearly states that it does work.
I can absolutely understand there will be holes and especially non documented things not working and/or bugs but this is something mentioned as an option that does not, in fact, work as documented at all.
You can also try this. (the opposite if condition, !obj.payload, does not work - well, big surprise)`
That's Flow, it understands some constructs but not others. The capability to understand the code has to be written into the OCaml code of Flow, and since there are an eternity of options they will never capture everything, you will always find holes. Avoid the holes, experiment. Flow and TS are hacks, full of compromises.
An example for another hack: if you ever use Array's filter(), there is exactly one (and only one!) construct that Flow understands: myArray.filter(Boolean). On top of there only being this one option, it does not even understand true booleans! You have to provide falsy values (undefined, null) to filter them out. It does not understand false. Flow is full of this kind of stuff.
Yeah i knew the filter thing
Most helpful comment
It doesn't work because it isn't implemented, like 10,000+ other things that _should_ work. As I said... by mixing the disjoint union with something different you leave the space covered by the current Flow implementation. As I said, there are many, many many such limitations. It's certainly okay to write issues, just don't expect anything. The incompleteness (of Flow) is a given. Just use the parts that work. Like, disjoint unions (without mixing them with something else, unless you are lucky and it works).