Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Could not decide which case to select #2892

Closed
stijnvanderlaan opened this issue Nov 25, 2016 · 15 comments
Closed

Could not decide which case to select #2892

stijnvanderlaan opened this issue Nov 25, 2016 · 15 comments

Comments

@stijnvanderlaan
Copy link

stijnvanderlaan commented Nov 25, 2016

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.

@vkurchatkin
Copy link
Contributor

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.

@stijnvanderlaan
Copy link
Author

stijnvanderlaan commented Nov 25, 2016

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.

@vkurchatkin
Copy link
Contributor

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

@stijnvanderlaan
Copy link
Author

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.

@gcanti
Copy link

gcanti commented Nov 25, 2016

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

@stijnvanderlaan
Copy link
Author

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.

@stijnvanderlaan
Copy link
Author

stijnvanderlaan commented Nov 26, 2016

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).

@rsolomon
Copy link

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.

Example here

@AndersDJohnson
Copy link

Any solution to this yet?

@Tomekmularczyk
Copy link

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",

@nodkz
Copy link
Contributor

nodkz commented Oct 30, 2017

Some built-in "private" type will be very useful.
Something like $PreferFirstCase<T> where T must be a union type.

[JUST PROPOSAL, NOT IMPLEMENTED]

@shinout
Copy link
Contributor

shinout commented Nov 8, 2017

+1 for $PreferFirstCase<T>!

@villesau
Copy link
Contributor

villesau commented Dec 30, 2018

The issue is actually about broken spread. There is a PR that will fix the issue: #7298

@sahara18
Copy link

Guess managed to rewrite via Object.assign

@goodmind goodmind added this to the New Object Spread semantics milestone Jul 17, 2019
@nmote
Copy link
Contributor

nmote commented Oct 25, 2019

This code no longer errors on master

@nmote nmote closed this as completed Oct 25, 2019
@facebook facebook deleted a comment from yatin-sodel May 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests