# typesafe-actions
Typesafe utilities designed to reduce types **verbosity**
and **complexity** in Redux Architecture.
_This library is part of the [React & Redux TypeScript Guide](https://github.com/piotrwitek/react-redux-typescript-guide)_ ecosystem :book:
[](https://www.npmjs.com/package/typesafe-actions)
[](https://semaphoreci.com/piotrekwitek/typesafe-actions)
[](https://david-dm.org/piotrwitek/typesafe-actions)
[](https://david-dm.org/piotrwitek/typesafe-actions?type=peer)
[](https://www.npmjs.com/package/typesafe-actions)
[](https://www.npmjs.com/package/typesafe-actions)
[](https://www.npmjs.com/package/typesafe-actions)
:star: _Found it useful? Want more updates?_ [**Show your support by giving a :star:**](https://github.com/piotrwitek/typesafe-actions/stargazers)
:tada: _Now updated to support **TypeScript v3.5**_ :tada:
---
**Features**
- **minimalistic** - according to `size-snapshot` (Minified: 3.48 KB, Gzipped: 1.03 KB), check also on [bundlephobia](https://bundlephobia.com/result?p=typesafe-actions)
- **secure and optimized** - no external dependencies, bundled in 3 different formats (`cjs`, `esm` and `umd` for browser) with separate optimized bundles for dev & prod (same as `react`)
- **focus on quality** - complete test-suite for an entire API surface containing regular runtime tests and extra type-tests to guarantee **type soundness** and to prevent regressions in the future TypeScript versions
- **focus on performance** - integrated performance benchmarks to guarantee that the computational complexity of types are in check and there are no slow-downs when your application grow `npm run benchmark:[2,10,50]`
**Codesandbox links**
- Reference Todo-App implementation using `typesafe-actions`: [Link](https://codesandbox.io/s/github/piotrwitek/typesafe-actions/tree/master/codesandbox)
- Starter to help reproduce bug reports: [Link](https://codesandbox.io/s/github/piotrwitek/typesafe-actions/tree/master/codesandbox)
---
## Table of Contents
- [Motivation](#motivation)
- [Installation](#installation)
- [Compatibility Notes](#compatibility-notes)
- [Contributing Guide](#contributing-guide)
- [Funding](#funding)
- [Tutorial](#tutorial)
- [Constants](#constants)
- [Actions](#actions)
- [1. Basic actions](#1-basic-actions)
- [2. FSA compliant actions](#2-fsa-compliant-actions)
- [3. Custom actions (non-standard use-cases)](#3-custom-actions-non-standard-use-cases)
- [Action Helpers](#action-helpers)
- [Using action-creators instances instead of type-constants](#using-action-creators-instances-instead-of-type-constants)
- [Using regular type-constants](#using-regular-type-constants)
- [Reducers](#reducers)
- [Extending internal types to enable type-free syntax with `createReducer`](#extending-internal-types-to-enable-type-free-syntax-with-createreducer)
- [Using createReducer API with type-free syntax](#using-createreducer-api-with-type-free-syntax)
- [Alternative usage with regular switch reducer](#alternative-usage-with-regular-switch-reducer)
- [Async-Flows](#async-flows)
- [With `redux-observable` epics](#with-redux-observable-epics)
- [With `redux-saga` sagas](#with-redux-saga-sagas)
- [API Docs](#api-docs)
- [Action-Creators API](#action-creators-api)
- [`action`](#action)
- [`createAction`](#createaction)
- [`createStandardAction`](#createstandardaction)
- [`createCustomAction`](#createcustomaction)
- [`createAsyncAction`](#createasyncaction)
- [Reducer-Creators API](#reducer-creators-api)
- [`createReducer`](#createreducer)
- [Action-Helpers API](#action-helpers-api)
- [`getType`](#gettype)
- [`isActionOf`](#isactionof)
- [`isOfType`](#isoftype)
- [Type-Helpers API](#type-helpers-api)
- [`ActionType`](#actiontype)
- [`StateType`](#statetype)
- [Migration Guides](#migration-guides)
- [`v3.x.x` to `v4.x.x`](#v3xx-to-v4xx)
- [`v2.x.x` to `v3.x.x`](#v2xx-to-v3xx)
- [`v1.x.x` to `v2.x.x`](#v1xx-to-v2xx)
- [Migrating from `redux-actions` to `typesafe-actions`](#migrating-from-redux-actions-to-typesafe-actions)
- [Recipes](#recipes)
- [Restrict Meta type in `action` creator](#restrict-meta-type-in-action-creator)
- [Compare to others](#compare-to-others)
- [`redux-actions`](#redux-actions)
- [MIT License](#mit-license)
---
## Motivation
When I started to combine Redux with TypeScript, I was trying to use [redux-actions](https://redux-actions.js.org/) to reduce the maintainability cost and boilerplate of **action-creators**. Unfortunately, the results were intimidating: incorrect type signatures and broken type-inference cascading throughout the entire code-base [(click here for a detailed comparison)](#redux-actions).
Existing solutions in the wild have been either **too verbose because of redundant type annotations** (hard to maintain) or **used classes** (hinders readability and requires using the **new** keyword 😱)
**So I created `typesafe-actions` to address all of the above pain points.**
The core idea was to design an API that would mostly use the power of TypeScript **type-inference** 💪 to lift the "maintainability burden" of type annotations. In addition, I wanted to make it "look and feel" as close as possible to the idiomatic JavaScript ❤️ , so we don't have to write the redundant type annotations that which will create additional noise in your code.
[⇧ back to top](#table-of-contents)
---
## Installation
```bash
// NPM
npm install typesafe-actions
// YARN
yarn add typesafe-actions
```
[⇧ back to top](#table-of-contents)
---
## Compatibility Notes
**TypeScript support**
- `typesafe-actions@1.X.X` - minimal TS v2.7
- `typesafe-actions@2.X.X` - minimal TS v2.9
- `typesafe-actions@3.X.X` - minimal TS v3.2
- `typesafe-actions@4.X.X` - minimal TS v3.2
**Browser support**
It's compatible with all modern browsers.
For older browsers support (e.g. IE <= 11) and some mobile devices you need to provide the following polyfills:
- [Object.assign](https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Obiekty/Object/assign#Polyfill)
- [Array.prototype.includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)
**Recommended polyfill for IE**
To provide the best compatibility please include a popular polyfill package in your application, such as `core-js` or `react-app-polyfill` for `create-react-app`.
Please check the `React` guidelines to learn how to do that: [LINK](https://reactjs.org/docs/javascript-environment-requirements.html)
A polyfill fo IE11 is included in our `/codesandbox` application.
[⇧ back to top](#table-of-contents)
---
## Contributing Guide
We are open for contributions. If you're planning to contribute please make sure to read the contributing guide: [CONTRIBUTING.md](/CONTRIBUTING.md)
[⇧ back to top](#table-of-contents)
---
## Funding
**Typesafe-Actions** is an independent open-source project created by people investing their free time for the benefit of our community.
If you are using **Typesafe-Actions** please consider donating as this will guarantee the project will be updated and maintained in the long run.
Issues can be funded by anyone interested in them being resolved. Reward will be transparently distributed to the contributor handling the task through the IssueHunt platform.
[](https://issuehunt.io/repos/110746954)
[⇧ back to top](#table-of-contents)
---
## Tutorial
To showcase the flexibility and the power of the **type-safety** provided by this library, let's build the most common parts of a typical todo-app using a Redux architecture:
> **WARNING**
> Please make sure that you are familiar with the following concepts of programming languages to be able to follow along: [Type Inference](https://www.typescriptlang.org/docs/handbook/type-inference.html), [Control flow analysis](https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#control-flow-based-type-analysis), [Tagged union types](https://github.com/Microsoft/TypeScript/wiki/What%27s-new-in-TypeScript#tagged-union-types), [Generics](https://www.typescriptlang.org/docs/handbook/generics.html) and [Advanced Types](https://www.typescriptlang.org/docs/handbook/advanced-types.html).
[⇧ back to top](#table-of-contents)
### Constants
> **RECOMMENDATION:**
> When using `typesafe-actions` in your project you won't need to export and reuse **string constants**. It's because **action-creators** created by this library have static property with **action type** that you can easily access using **actions-helpers** and then use it in reducers, epics, sagas, and basically any other place. This will simplify your codebase and remove some boilerplate code associated with the usage of **string constants**. Check our `/codesandbox` application to learn some best-practices to create such codebase.
**Limitations of TypeScript when working with string constants** - when using **string constants** as action `type` property, please make sure to use **simple string literal assignment with const**. This limitation is coming from the type-system, because all the **dynamic string operations** (e.g. string concatenation, template strings and also object used as a map) will widen the literal type to its super-type, `string`. As a result this will break contextual typing for **action** object in reducer cases.
```ts
// Example file: './constants.ts'
// WARNING: Incorrect usage
export const ADD = prefix + 'ADD'; // => string
export const ADD = `${prefix}/ADD`; // => string
export default {
ADD: '@prefix/ADD', // => string
}
// Correct usage
export const ADD = '@prefix/ADD'; // => '@prefix/ADD'
export const TOGGLE = '@prefix/TOGGLE'; // => '@prefix/TOGGLE'
export default ({
ADD: '@prefix/ADD', // => '@prefix/ADD'
} as const) // working in TS v3.4 and above => https://github.com/Microsoft/TypeScript/pull/29510
```
[⇧ back to top](#table-of-contents)
### Actions
Different projects have different needs, and conventions vary across teams, and this is why `typesafe-actions` was designed with flexibility in mind. It provides three different major styles so you can choose whichever would be the best fit for your team.
#### 1. Basic actions
`action` and `createAction` are creators that can create **actions** with predefined properties ({ type, payload, meta }). This make them concise but also opinionated.
Important property is that resulting **action-creator** will have a variadic number of arguments and preserve their semantic names `(id, title, amount, etc...)`.
This two creators are very similar and the only real difference is that `action` **WILL NOT WORK** with **action-helpers**.
```ts
import { action, createAction } from 'typesafe-actions';
export const add = (title: string) => action('todos/ADD', { id: cuid(), title, completed: false });
// add: (title: string) => { type: "todos/ADD"; payload: { id: string, title: string, completed: boolean; }; }
export const add = createAction('todos/ADD', action => {
// Note: "action" callback does not need "type" parameter
return (title: string) => action({ id: cuid(), title, completed: false });
});
// add: (title: string) => { type: "todos/ADD"; payload: { id: string, title: string, completed: boolean; }; }
```
#### 2. FSA compliant actions
This style is aligned with [Flux Standard Action](https://github.com/redux-utilities/flux-standard-action), so your **action** object shape is constrained to `({ type, payload, meta, error })`. It is using **generic type arguments** for `meta` and `payload` to simplify creation of type-safe action-creators.
It is important to notice that in the resulting **action-creator** arguments are also constrained to the predefined: `(payload, meta)`, making it the most opinionated creator.
> **TIP**: This creator is the most compatible with `redux-actions` in case you are migrating.
```ts
import { createStandardAction } from 'typesafe-actions';
export const toggle = createStandardAction('todos/TOGGLE')