Reputation: 12433
I have this type:
export type Action<P> = {
type: string,
payload: P
}
I now need to pass a return type to Dispatch
, which I want to be an Action<number>
or an Action<boolean>
Why can I not do this?
const mapDispatchToProps = (dispatch: Dispatch<Action<number | boolean>>) => {
return bindActionCreators(
{
updateAlertModalHeight: updateAlertModalHeight,
updateAlertModalIsOpen: updateAlertModalIsOpen,
updateHasYesNo: updateAlertModalHasYesNo
},
dispatch
)
}
It doesn't let me do Action<number> | Action<boolean>
either.
The error is:
Cannot call
bindActionCreators
because number [1] is incompatible with boolean [2] in type argumentP
[3] of type argumentA
[4] of propertyupdateAlertModalHeight
of type argumentC
. (References: [1] [2] [3] [4])flow
Where am I going wrong?
Here is updateAlertModalHeight
from the error message:
export const updateAlertModalHeight: ActionCreator<Action<number>, number> = (
payload
) => ({
type: UPDATE_ALERT_MODAL_HEIGHT,
payload
})
and:
export const updateAlertModalIsOpen: ActionCreator<Action<boolean>, boolean> = (
payload
) => ({
type: UPDATE_ALERT_MODAL_IS_OPEN,
payload
})
the two action creators have a different type and Flow is not allowing them to be used in the same bindActionCreators
call. How do I get flow to allow it?
It all seems to work when I make everything this type:
export type ActionT = Action<boolean | string | number>
export const updateAlertModalIsOpen: ActionCreator<ActionT, boolean> = (
payload
) => ({
type: UPDATE_ALERT_MODAL_IS_OPEN,
payload
})
and:
const mapDispatchToProps = (dispatch: Dispatch<ActionT>) => {
But that is less specific typing which sucks because it doesn't describe which type of action is being returned.
I would like to edit the redux type definitions to allow multiple Action types but it is difficult to understand what change to make.
Upvotes: 3
Views: 124
Reputation: 12433
To move forward I have gotten flow to be quiet by getting rid of my custom type Action
and am importing Redux's type Action
E.G. import type { Action } from 'redux'
. The one Redux uses is
declare export type Action<T> = {
type: T
}
I find it a bit weird because the type
field is always a string in a redux action. E.G 'UPDATE_ALERT_MODAL_IS_OPEN'
. So it is pointless to specify string. I always type them with Action<string>
My action creator becomes:
export const updateAlertModalIsOpen: ActionCreator<Action<string>, boolean> = (
payload
) => ({
type: UPDATE_ALERT_MODAL_IS_OPEN,
payload
})
mapDispatchToProps becomes:
const mapDispatchToProps = (dispatch: Dispatch<Action<string>>) => {
return bindActionCreators(
{
updateAlertModalIsOpen,
updateAlertModalHeight,
updateHasYesNo: updateAlertModalHasYesNo
},
dispatch
)
}
Upvotes: 0
Reputation: 9487
The generic type argument of dispatch
is invariant, which means that it cannot be anything else than exactly what you specified — in particular, it cannot be specialized to number
from boolean|number
. Intuitively, the type information number
is more specific than boolean|number
.
The actual problem is the type of bindActionCreators
, which enforces the same type argument as you give to dispatch
to both updateAlertModalHeight
, and updateAlertModalIsOpen
. But these functions take different types of arguments! One takes a number, and one a Boolean, hence the type checker can't infer the types correctly. It works when you change the type argument of updateAlertModalHeight
and updateAlertModalIsOpen
to allow for being called with either type, i.e. saying "it's OK if you pass a number, I'll ignore it, and only act if you pass me a boolean." The type system sees it can pass both a number
and boolean
to the functions and therefore everything type checks.
The real solution here that does not rely on changing the argument types, would be to do two calls to bindActionCreators
, once with a number
-dispatch
and once with boolean
-dispatch
.
Upvotes: 1