Monday, July 31, 2017

Redux combineReducers compiler error

If a Redux action is added with properties other than the type property, here it's payload..

export interface UserActions extends Action {
    type: UserKind;
    payload: UserPayload;
}

..TypeScript will raise an error on combineReducers:



To fix the compilation error, make the Redux action property be optional:

export interface UserActions extends Action {
    type: UserKind;
    payload?: UserPayload;
}


Result:




However, TypeScript won't be able to catch missed properties, it won't raise an error that we didn't pass the payload property on User 6:




To fix that, remove the optional indicator on Redux action:

export interface UserActions extends Action {
    type: UserKind;
    payload: UserPayload;
}


Result, TypeScript is now able to catch missed properties:




However, we are back to this error on combineReducers:




To comply with combineReducers requirement of Reducer actions containing just a type property only, create another interface that has optional properties (e.g., payload), and use that for the Redux reducer's action parameter (UserActionsNoPayload). And then on UserActions, inherit UserActionsNoPayload from UserActions:



Now, no more combineReducers compilation error, and the compiler is still able to catch missed properties:




Here are the TypeScript definitions for the routeToUser:

export interface StateProps {
    user: Dto.Client.User;
    component: Component;
    counter: Dto.Client.Counter;
}


export interface ActionProps {
    routeToUser: UserDispatcher;
    doCounter: CounterDispatcher;
}


interface Props extends StateProps, ActionProps {
}


export type Dispatcher = (action: UserActions | CounterActions) => void;
export type UserDispatcher = (action: UserActions) => void;
export type CounterDispatcher = (action: CounterActions) => void;


export default class ReduxFirstRouterApp extends React.Component<Props, {}> {



And here is how are the states and dispatchers are mapped as properties to a component via Redux's connect:
const mapStateToProps = ({user, component, counter}: StateProps) => ({user, component, counter});

const mapDispatchToProps = (dispatch: Dispatcher): ActionProps => ({
    routeToUser: (action: UserActions) => dispatch(action),
    doCounter: (action: CounterActions) => dispatch(action)
});

this.AppContainer = connect(mapStateToProps, mapDispatchToProps)(ReduxFirstRouterApp);


Happy Coding!


P.S.

At first, I tried creating UserActions with complete properties, and then made UserActionsNoPayload inherits UserActions and define payload as optional on UserActionsNoPayload; however, it's a compiler error:


No comments:

Post a Comment