1.2.1 • Published 6 years ago
@housinganywhere/safe-redux v1.2.1
safe-redux :evergreen_tree:
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!
Install
yarn add @housinganywhere/safe-redux
npm i @housinganywhere/safe-reduxUse
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;Type utils
safe-redux also provides some type utils to work with Redux.
BindAction
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);Differences with rex-tils
- Actions created by
createActionare compliant withflux-standard-actions, meaning they have anerrorproperty set totruewhen the payload isinstanceof Errorand might have ametaproperty. - Added
handleActionsto create type safe reducers. - Smaller API.
safe-reduxonly exports a few functions and types:- Functions:
createActionandhandleActions. - Types:
Action,ActionsUnion,ActionsOfType,HandlerandBindAction.
- Functions: