Flow: Union Type Unable To Decide Which Case To Select

Created on 12 May 2017  路  3Comments  路  Source: facebook/flow

Hi,

I'm trying to use Flow to type check my reducers in a Redux App and have a reducer which looks like:

import { SET_ADHOC_TASKS } from '../constants/ActionTypes';
import type { Action, AdHocTaskType } from '../constants/FlowTypes';

const defaultState = [];
const AdHocTaskReducer = (state: AdHocTaskType[] = defaultState, action: Action) => {
    switch (action.type) {
        case SET_ADHOC_TASKS:
            // Update the list of AdHoc tasks with the latest details.
            return action.message.tasks;
        default:
            return state;
    }
};

And my Action type is a union type

export type Action =
    | SetAdhocTasksAction
    | SetAlterationsAction
    | SetUsersAction
    ... many more actions

With the individual actions looking like:

export type SetAdhocTasksAction = {|
    type: 'SET_ADHOC_TASKS',
    message: AdHocTasksType
|}

export type SetAlterationsAction = {|
    type: 'SET_ALTERATIONS',
    message: AlterationsType
|}

export type SetUsersAction = {|
    type: 'SET_USERS',
    message: UserType[]
|}

When I run Flow I get errors like this:

app/reducers/__tests__/AdHocTaskReducer.test.js:15
 15:         const nextState = AdHocTaskReducer([], action);
                                                    ^^^^^^ object literal. Could not decide which case to select
  8: const AdHocTaskReducer = (state: AdHocTaskType[] = defaultState, action: Action) => {
                                                                              ^^^^^^ union type. See: app/reducers/AdHocTaskReducer.js:8
  Case 1 may work:
  270:     | SetAdhocTasksAction
             ^^^^^^^^^^^^^^^^^^^ SetAdhocTasksAction. See: app/constants/FlowTypes.js:270
  But if it doesn't, case 2 looks promising too:
  271:     | SetAlterationsAction
             ^^^^^^^^^^^^^^^^^^^^ SetAlterationsAction. See: app/constants/FlowTypes.js:271
  Please provide additional annotation(s) to determine whether case 1 works (or consider merging it with case 2):
   13:             message: [mockUser1]
                            ^^^^^^^^^^^ array literal
   12:             type: SET_USERS,
                         ^^^^^^^^^ identifier `SET_USERS
````

From a test case that looks like

```javascript
    test('returns the state if no action matches', () => {
        const action = {
            type: SET_USERS,
            message: [mockUser1]
        };
        const nextState = AdHocTaskReducer([], action);
        expect(nextState).toEqual([]);
    });

I don't understand why Flow can't use the type field in the object to differentiate between the Actions and choose the appropriate type. Is this a limitation of Flow or am I doing something wrong?

It seems to work fine in a simple example here - https://flow.org/try/#0PQKgBAAgZgNg9gdzCYAoVAXAngBwKZgDOeGACgE5gC8YA3qgJDb4BcYA5AMoCiAKgPqkASuwA0qMJLABbPIUIBDAOZ429KVPZR2bQhnIBLAHZKJUgL6pLmXAWIYAktOUEa6prbZc+-BwFkAQQBxbjEzSVl5FzUlXX1jJUtrKABXIwBjDAM4IzAoODgACgAPXRIKMAAfIhInFwBKOnCwAygwEoA6ZlcqGm8BYXZG9Q0pchIU8lzijsjFFQ6oAG5m8zA8GGIm0YZxjEnp2bl5vA6lJbBVq1QgA

And the docs for disjoint unions here suggest it should be fine - https://flow.org/en/docs/types/unions/

Most helpful comment

So I had some more time to play with this. Replacing the constants imports in my tests with strings (so everywhere there was a SET_USERS I replaced it with "SET_USERS" fixes this issue)

I could leave the constants in the reducer switch statement though.

Is this a bug or feature?

All 3 comments

See this blog post which goes into more detail about what this error means: https://flow.org/blog/2016/07/01/New-Unions-Intersections/

How are you bringing SET_USERS into scope in your test?

Thanks for the link I'll have a read.

To answer your question SET_USERS is brought into scope via an import as follows:

import { SET_ADHOC_TASKS, SET_USERS } from '../../constants/ActionTypes';

and the definition in constants/ActionTypes looks like

export const SET_USERS = 'SET_USERS';

Is there an issue with doing it like this?

So I had some more time to play with this. Replacing the constants imports in my tests with strings (so everywhere there was a SET_USERS I replaced it with "SET_USERS" fixes this issue)

I could leave the constants in the reducer switch statement though.

Is this a bug or feature?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

funtaps picture funtaps  路  3Comments

ctrlplusb picture ctrlplusb  路  3Comments

jamiebuilds picture jamiebuilds  路  3Comments

iamchenxin picture iamchenxin  路  3Comments

damncabbage picture damncabbage  路  3Comments