As a type argument we specify the typeof Actions
.
Note: we still actually need to define the Actions
object
import { createActionMaker } from "safe-redux-actions"
const makeAction = createActionMaker<typeof Actions>();
Here we can build the Action
object. Actions
keys are supposed to be corresponding to the action types.
That means the action maker makeAction
will only allow as an argument a string which is also a keyof Actions
.
export const Actions = {
add: (num: number) => makeAction("add", num), // { type: "add", payload: num }
increase: () => makeAction("increase"), // { type: "increase" }
decrease: () => makeAction("decrease") // { type: "decrease" }
};
The exported Action
type will be used as input type for your reducer(s).
The typings for the payload and the action type will be automatically inferred.
import { createActionMaker, SingleAction } from "safe-redux-actions"
//...
export type Action = SingleAction<typeof Actions>;
This is how it will look like in your reducer:
import { Action } from "src/store/actions";
export interface CounterState {
counter: number;
}
export const initialState: CounterState = {
counter: 0
};
export default function rootReducer(state = initialState, action: Action) {
switch (action.type) { // "add" | "increase" | "decrease"
case "add":
return {
...state,
counter: state.counter + action.payload // payload is number
};
case "increase":
// payload is unknown
return {
...state,
counter: state.counter + 1
};
case "decrease":
// payload is unknown
return {
...state,
counter: state.counter - 1
};
default:
return state;
}
}
The goal of this package was making possible to easily type a basic Redux implementation with as little code as possible.
The following is the actual full code of this package:
export const createActionMaker = <A>() => <T extends keyof A, P>(type: T, payload?: P) => ({ type, payload: payload! });
export type SingleAction<T extends Record<string, (...p: any) => any>> = ReturnType<T[keyof T]>;