What is the best way to cast the action
parameter in a redux reducer with typescript? There will be multiple action interfaces that can occur that all extend a base interface with a property type. The extended action interfaces can have more properties that are all different between the action interfaces. Here is an example below:
interface IAction {
type: string
}
interface IActionA extends IAction {
a: string
}
interface IActionB extends IAction {
b: string
}
const reducer = (action: IAction) {
switch (action.type) {
case 'a':
return console.info('action a: ', action.a) // property 'a' does not exists on type IAction
case 'b':
return console.info('action b: ', action.b) // property 'b' does not exists on type IAction
}
}
The problem is that action
needs to be cast as a type that has access to both IActionA
and IActionB
so the reducer can use both action.a
and action.a
without throwing an error.
I have several ideas how to work around this issue:
- Cast
action
toany
. - Use optional interface members.
example:
interface IAction {
type: string
a?: string
b?: string
}
- Use different reducers for every action type.
What is the best way to organize Action/Reducers in typescript? Thank you in advance!
you could do the following things
if you expect one of
IActionA
orIActionB
only, you can limit the type at least and define your function asNow, the thing is, you still have to find out which type it is. You can totally add a
type
property but then, you have to set it somewhere, and interfaces are only overlays over object structures. You could create action classes and have the ctor set the type.Otherwise you have to verify the object by something else. In your case you could use
hasOwnProperty
and depending on that, cast it to the correct type:This would still work when compiled to JavaScript.
I have an
Action
interfaceI have a
createAction
function:I have an action type constant:
And I have an interface for the action (check out the cool use of
typeof
):I have an action creator function:
Now my reducer looks something like this:
And I have a reducer function per action:
Here is how can you do it with
redux-fluent
:With Typescript 2's Tagged Union Types you can do the following
I am the author of ts-redux-actions-reducer-factory and would present you this as an another solution on top of the others. This package infers the action by action creator or by manually defined action type and - that's new - the state. So each reducer takes aware of the return type of previous reducers and represents therefore a possible extended state that must be initialized at the end, unless done at beginning. It is kind of special in its use, but can simplify typings.
But here a complete possible solution on base of your problem:
With Typescript v2, you can do this pretty easily using union types with type guards and Redux's own Action and Reducer types w/o needing to use additional 3rd party libs, and w/o enforcing a common shape to all actions (e.g. via
payload
).This way, your actions are correctly typed in your reducer catch clauses, as is the returned state.
Things is basically what @Sven Efftinge suggests, while additionally checking the reducer's return type.