NOTE: this library is based on @martin_hotell's rex-tils library and his article Improved Redux type safety with TypeScript 2.8.
Instead of telling the program what types it should use, types are inferred from the implementation, so type checker gets out of our way!
yarn add @housinganywhere/safe-redux
npm i @housinganywhere/safe-redux
Define the actions:
// src/pages/MyPage/actions.ts
import { ActionsUnion, createAction } from '@housinganywhere/safe-redux';
export const INC = '[counter] increment';
export const DEC = '[counter] decrement';
export const INC_BY = '[counter] increment_by';
export const WITH_META = '[counter] with_meta';
export const Actions = {
inc: () => createAction(INC),
dec: () => createAction(DEC),
incBY: (by: number) => createAction(INC_BY, by),
withMeta: (by: number, meta: string) => createAction(WITH_META, by, meta),
};
export type Actions = ActionsUnion<typeof Actions>;
export type ActionTypes =
| typeof INC
| typeof DEC
| typeof INC_BY
| typeof WITH_META;
Handle the actions:
// src/pages/MyPage/reducer.ts
import { handleActions, Handler } from '@housinganywhere/safe-redux';
import { User } from '../types';
import { INC, DEC, INC_BY, WITH_META, Actions, ActionTypes } from './actions';
interface State {
count: number;
}
const initialState: State = {
count: 0,
};
// `Handler` type can be used when you don't want to define the handlers inline
const handleIncBy: Handler<State, typeof INC_BY, Actions> = (
{ count },
{ payload },
) => ({ count: count + payload });
const reducer = handleActions<State, ActionTypes, Actions>(
{
[INC]: ({ count }) => ({ count: count + 1 }),
[DEC]: ({ count }) => ({ count: count - 1 }),
[INC_BY]: handleIncBy,
[WITH_META]: ({ count }, { payload }) => ({ count: count + payload }),
},
initialState,
);
export default reducer;
safe-redux
also provides some type utils to work with Redux.
Changes the return type of an action creator to void
. In the context of a
component the only important part of an action is the types of it's arguments.
We don't rely on the return type.
// src/pages/MyPage/actions.ts
import { ActionsUnion, createAction } from '@housinganywhere/safe-redux';
export const INC = '[counter] increment';
export const DEC = '[counter] decrement';
export const INC_BY = '[counter] increment_BY';
export const Actions = {
incBy: (by: number) => createAction(INC_BY, { by }),
};
export type Actions = ActionsUnion<typeof Actions>;
// src/pages/MyPage/MyPage.container.ts
import { connect } from 'react-redux';
import { BindAction } from '@housinganywhere/safe-redux';
import { Actions } from './actions';
import MyPage from './MyPage';
interface StateProps {
count: number;
}
interface DispatchProps {
incBy: BindAction<typeof Actions.incBy>; // (arg: number) => void
}
type MyPageProps = StateProps & DispatchProps;
export default connect<StateProps, DispatchProps>(
(s) => ({ count: s.count }),
{ incBy: Actions.incBy },
)(MyPage);
- Actions created by
createAction
are compliant withflux-standard-actions
, meaning they have anerror
property set totrue
when the payload isinstanceof Error
and might have ameta
property. - Added
handleActions
to create type safe reducers. - Smaller API.
safe-redux
only exports a few functions and types:- Functions:
createAction
andhandleActions
. - Types:
Action
,ActionsUnion
,ActionsOfType
,Handler
andBindAction
.
- Functions: