This
export type InitState = {
type: "init",
lang: string,
}
export type SecondState = {
type: "second",
lang: string,
}
export type State = InitState | SecondState
const transform = (input:State, lang:string) : State => {
return {
...input,
lang
}
}
let state: State = {
type: "init",
lang: "en"
}
let state2 = transform(state, "nl")
Results in the next error.
47: return {
^ object literal. Could not decide which case to select
46: const transform = (input:State, lang:string) : State => {
^^^^^ union type
Case 1 may work:
44: export type State = InitState | SecondState
^^^^^^^^^ InitState
But if it doesn't, case 2 looks promising too:
44: export type State = InitState | SecondState
^^^^^^^^^^^ SecondState
Please provide additional annotation(s) to determine whether case 1 works (or consider merging it with case 2):
.type
Al the cases have lang
, so I think it should return the same type as it gets, like the next situation, which works fine:
const transform = (input:State, lang:string) : State => {
return input
}
Casting the output to State
or typeof input
does not resolve the issue.
Here is a problem: when you something to have type State
it should either have type State
already, or it should be one of InitState
or SecondState
. Result of object is none of those, it has type { type: 'init' | 'second', lang: string }
or something like this.
But when SecondState
has additional parameters you have to make them optional because of this transform
. This situation is just a illustration what happens in redux and transform
is a reducer. It would be great to have a reducer with differente states, and be able te change the state without actually knowing which state you are because al cases are compatible.
be able te change the state without actually knowing which state you are because al cases are compatible
Well, you can actually change (mutate) it, but object spread merges branches together
Off course, but redux (and functional programming in general) is build around immutability to knows when changes has been made. I understand it's hard for flow to choose which type it has to output, but it is not impossible, it has the information to decide, right? Unfortunately for me it is now impossible to add flow type to this situation. Thanks for your feedback.
You could refine input
function transform(input: State, lang: string): State {
if (state.type === 'init')
return {
type: 'init',
...input,
lang
}
return {
type: 'second',
...input,
lang
}
}
Yes, but I have to much types, it would only make the code less easy to manage which is not really beneficial. I think I am going to extract that code into another reducer. Still this enhancement would be very welcome.
I was thinking some more: Why does flow needs to know which one it is? Can't the function just return a union type in the case where it's undecidable? It only needs to check if the newly created object can match InitState
or SecondState
(and watching the error it looks like it already knows).
Apologies for bumping such an old thread, but I'm curious if anyone has found a workaround for this. Manual refinement works fine if the disjoint union has 2 or 3 cases, but it not reasonable for larger sets. I'm not clear on why creating a new object from an old one via spread and changing one property (shared amongst all cases) would not pass Flow checks.
Any solution to this yet?
export default connect(
(state: ReduxState) => ({
modalPayload: state.ui.modalPayload, // Error:(15, 16) Flow: function call. Could not decide which case to select intersection type
}),
)(InfoContent);
ReduxState is a state declared in flow typed directory as global. I don't know what the error means. After I upgraded "flow-bin": "^0.57.3",
Some built-in "private" type will be very useful.
Something like $PreferFirstCase<T>
where T must be a union type.
[JUST PROPOSAL, NOT IMPLEMENTED]
+1 for $PreferFirstCase<T>
!
The issue is actually about broken spread. There is a PR that will fix the issue: https://github.com/facebook/flow/pull/7298
Guess managed to rewrite via Object.assign
This code no longer errors on master
Most helpful comment
I was thinking some more: Why does flow needs to know which one it is? Can't the function just return a union type in the case where it's undecidable? It only needs to check if the newly created object can match
InitState
orSecondState
(and watching the error it looks like it already knows).