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/
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?
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_USERSI 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?