Typesafe Action Creators for Redux
Typesafe-actions is a lightweight utility library designed to simplify and enhance type safety in Redux and Flux architectures, particularly within TypeScript projects. Currently at version 5.1.0, it focuses on reducing boilerplate and complexity in defining actions and reducers. The library ships with minimal external dependencies, emphasizing a secure and performant codebase. It provides robust tools for creating fully typesafe action creators (including synchronous and asynchronous actions), and streamlined reducer definitions. Typesafe-actions differentiates itself by offering extensive type-testing to ensure soundness across TypeScript versions and provides optimized builds for various environments (CJS, ESM, UMD). While it offers a powerful approach to Redux type safety, it's worth noting that Redux Toolkit is the officially recommended solution by the Redux team, which also offers strong TypeScript integration.
Common errors
-
TS2305: Module '"typesafe-actions"' has no exported member 'RootAction'.
cause The `RootAction` type helper was not properly exported or inferred, or there was a change in how `RootAction` should be defined and extended within the module.fixEnsure `RootAction` is correctly defined and exported in your application's types (e.g., `export type RootAction = ActionType<typeof import('./actions').default>;`) and extend the `typesafe-actions` module's `Types` interface if using the type-free `createReducer` syntax. This was specifically resolved in `v4.1.2` for some scenarios. -
Error: createReducer cannot be called when handling more than 2 actions
cause An internal limitation or bug in older versions of `createReducer` that prevented it from correctly processing handlers for a large number of actions.fixUpgrade `typesafe-actions` to version `v4.1.3` or higher, which includes a fix for this specific issue. -
Error: Actions may not have an undefined "type" property.
cause This error typically occurs when an action creator is called without providing a type string or if the type property is implicitly `undefined`. It could also indicate an incorrect migration path for action creators.fixEnsure that all action creators, especially those migrated from older APIs or custom implementations, explicitly define a string `type` property. Review the `v5` API for `createAction` to ensure correct usage.
Warnings
- breaking In `v5`, all deprecated `v4` action creator functions (`createAction`, `createStandardAction`, `createCustomAction`) have been moved under a named `deprecated` import. Direct imports of these functions from the root are no longer available.
- breaking In `v4.4.2`, `createStandardAction` was renamed to `createAction`, and its `.map` method was removed. If upgrading from versions prior to `v4.4.2`, code using `createStandardAction` or its `.map` method will break.
- gotcha The official documentation and tutorial for `typesafe-actions` are currently outdated for `v5.x.x` and reflect `v4` APIs. Users are directed to a GitHub issue for temporary `v5.x.x` API documentation.
- gotcha The Redux team officially recommends Redux Toolkit (`@reduxjs/toolkit`) for building Redux applications, which is also fully type-safe and aims to reduce boilerplate. Users considering `typesafe-actions` might want to evaluate Redux Toolkit as an alternative.
Install
-
npm install typesafe-actions -
yarn add typesafe-actions -
pnpm add typesafe-actions
Imports
- createAction
import { createStandardAction } from 'typesafe-actions'import { createAction } from 'typesafe-actions' - createReducer
import { createReducer } from 'typesafe-actions' - createAsyncAction
import { createAsyncAction } from 'typesafe-actions' - deprecated
import { deprecated } from 'typesafe-actions'; const { createAction, createStandardAction } = deprecated; - ActionType
import { ActionType } from 'typesafe-actions'
Quickstart
import { createAction, createReducer, ActionType } from 'typesafe-actions';
// 1. Define Actions
const increment = createAction('INCREMENT')<number>();
const decrement = createAction('DECREMENT')<void>();
const reset = createAction('RESET')();
const actions = {
increment,
decrement,
reset
};
type RootAction = ActionType<typeof actions>;
interface CounterState {
count: number;
}
const initialState: CounterState = {
count: 0
};
// 2. Create a Reducer
const counterReducer = createReducer<CounterState, RootAction>(initialState)
.handleAction(increment, (state, action) => ({
...state,
count: state.count + action.payload
}))
.handleAction(decrement, (state) => ({
...state,
count: state.count - 1
}))
.handleAction(reset, (state) => ({
...state,
count: 0
}));
// Example Usage (simulate Redux store)
let currentState = initialState;
console.log('Initial State:', currentState);
currentState = counterReducer(currentState, actions.increment(5));
console.log('After Increment by 5:', currentState);
currentState = counterReducer(currentState, actions.decrement());
console.log('After Decrement:', currentState);
currentState = counterReducer(currentState, actions.reset());
console.log('After Reset:', currentState);