Redux TypeScript Reducers
Fluent syntax for defining typesafe reducers on top of typescript-fsa.
Installation
npm install --save redux-typescript-reducers typescript-fsa
Usage
This library allows you to define reducers by chaining a series of handlers for different action types and optionally providing an initial value. It builds on top of and assumes familiarity with the excellent typescript-fsa.
Suppose we have the setup:
;const actionCreator = ;interface Statename: string;balance: number;isFrozen: boolean;const INITIAL_STATE: State =name: "Untitled"balance: 0isFrozen: false;const setName = actionCreator<string>"SET_NAME";: State {return ...state name ;}const addBalance = actionCreator<number>"ADD_BALANCE";: State {return ...state balance: statebalance + addedBalance ;}const setIsFrozen = actionCreator<boolean>"SET_IS_FROZEN";: State {return ...state isFrozen ;}
Using vanilla typescript-fsa
, you would most likely define a reducer as follows:
;;: State {ifreturn ;else ifreturn ;else ifreturn ;elsereturn state;}
With redux-typescript-reducers
, this is exactly equivalent to the following code:
;const reducer =;
Everything is typesafe. If the types of the action payload and handler don't line up, then TypeScript will complain.
The reducer builders are immutable. Each call to .case()
returns a new reducer rather than
modifying the callee.
API
reducerWithInitialState(initialState)
Starts a reducer builder-chain which uses the provided initial state if passed undefined
as its
state. For example usage, see the "Usage" section above.
reducerWithoutInitialState()
Starts a reducer builder-chain without special logic for an initial state. undefined
will be
treated like any other value for the state.
Redux seems to really want you to provide an initial state for your reducers. Its createStore
API
encourages it and combineReducers
function enforces it. For the Redux author's reasoning behind
this, see this thread. For this reason,
reducerWithInitialState
will likely be the more common choice, but the option to not provide an
initial state is there in case you have some means of composing reducers for which intial state is
unnecessary.
Note that since the type of the state cannot be inferred from the initial state, it must be provided as a type parameter:
const reducer = reducerWithoutInitialState<State>;
upcastingReducer()
Starts a reducer builder-chain which produces a reducer whose return type is a supertype of the input state. This is most useful for handling a state which may be in one of several "modes", each of which responds differently to actions and can transition to the other modes. Many programs will not have a use for this.
Example usage:
type State = StoppedState | RunningState;interface StoppedStatetype: "STOPPED";interface StartedStatetype: "STARTED";count: number;const startWithCount = actionCreator<number>"START_WITH_COUNT";const addToCount = actionCreator<number>"ADD_TO_COUNT";const stop = actionCreator<void>"STOP";: State {return type: "STARTED" count ;}: State {return ...state count: statecount + count ;}: State {return type: "STOPPED" ;}const stoppedReducer = upcastingReducer<StoppedState State>;const startedReducer = upcastingReducer<StartedState State>;: State {if statetype === "STOPPED"return ;else if statetype === "STARTED"return ;elsethrow "Unknown state";}
Copyright © 2017 David Philipson