{"id":13452529,"url":"https://github.com/piotrwitek/typesafe-actions","last_synced_at":"2025-05-14T00:11:41.136Z","repository":{"id":43190096,"uuid":"110746954","full_name":"piotrwitek/typesafe-actions","owner":"piotrwitek","description":"Typesafe utilities for \"action-creators\" in Redux / Flux Architecture","archived":false,"fork":false,"pushed_at":"2024-01-15T11:56:48.000Z","size":1710,"stargazers_count":2405,"open_issues_count":41,"forks_count":98,"subscribers_count":14,"default_branch":"master","last_synced_at":"2025-04-10T03:44:59.635Z","etag":null,"topics":["action-creator","javascript","redux","redux-actions","static-typing","typescript"],"latest_commit_sha":null,"homepage":"https://codesandbox.io/s/github/piotrwitek/typesafe-actions/tree/master/codesandbox","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/piotrwitek.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":".github/CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":null,"patreon":"piotrekwitek","open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":"piotrwitek","otechie":null,"custom":["https://www.buymeacoffee.com/piotrekwitek"]}},"created_at":"2017-11-14T21:30:09.000Z","updated_at":"2025-03-21T21:17:13.000Z","dependencies_parsed_at":"2024-06-18T11:19:45.714Z","dependency_job_id":"d5886d9f-ecd4-424b-a930-8ffe723ca3b5","html_url":"https://github.com/piotrwitek/typesafe-actions","commit_stats":{"total_commits":340,"total_committers":28,"mean_commits":"12.142857142857142","dds":0.08235294117647063,"last_synced_commit":"a1fe54bb150ac1b935bb9ca78361d2d024d2efaf"},"previous_names":["piotrwitek/ts-redux-actions"],"tags_count":40,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrwitek%2Ftypesafe-actions","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrwitek%2Ftypesafe-actions/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrwitek%2Ftypesafe-actions/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/piotrwitek%2Ftypesafe-actions/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/piotrwitek","download_url":"https://codeload.github.com/piotrwitek/typesafe-actions/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254044442,"owners_count":22005159,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["action-creator","javascript","redux","redux-actions","static-typing","typescript"],"created_at":"2024-07-31T07:01:26.663Z","updated_at":"2025-05-14T00:11:36.128Z","avatar_url":"https://github.com/piotrwitek.png","language":"TypeScript","funding_links":["https://patreon.com/piotrekwitek","https://issuehunt.io/r/piotrwitek","https://www.buymeacoffee.com/piotrekwitek","https://www.buymeacoffee.com/piotrekwitek)_","https://www.patreon.com/piotrekwitek","https://issuehunt.io/repos/110746954"],"categories":["TypeScript"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\n# typesafe-actions\n\nTypesafe utilities designed to reduce types **verbosity**\nand **complexity** in Redux Architecture.\n\n_This library is part of the [React \u0026 Redux TypeScript Guide](https://github.com/piotrwitek/react-redux-typescript-guide)_ ecosystem :book:  \n\n[![Latest Stable Version](https://img.shields.io/npm/v/typesafe-actions.svg)](https://www.npmjs.com/package/typesafe-actions)\n[![NPM Downloads](https://img.shields.io/npm/dm/typesafe-actions.svg)](https://www.npmjs.com/package/typesafe-actions)\n[![NPM Downloads](https://img.shields.io/npm/dt/typesafe-actions.svg)](https://www.npmjs.com/package/typesafe-actions)\n[![Bundlephobia Size](https://img.shields.io/bundlephobia/minzip/typesafe-actions.svg)](https://www.npmjs.com/package/typesafe-actions)\n\n[![Build Status](https://semaphoreci.com/api/v1/piotrekwitek/typesafe-actions/branches/master/shields_badge.svg)](https://semaphoreci.com/piotrekwitek/typesafe-actions)\n[![Dependency Status](https://img.shields.io/david/piotrwitek/typesafe-actions.svg)](https://david-dm.org/piotrwitek/typesafe-actions)\n[![License](https://img.shields.io/npm/l/typesafe-actions.svg?style=flat)](https://david-dm.org/piotrwitek/typesafe-actions?type=peer)\n[![Join the community on Spectrum](https://withspectrum.github.io/badge/badge.svg)](https://spectrum.chat/typesafe-actions)\n\n_Found it useful? Want more updates?_\n\n[**Show your support by giving a :star:**](https://github.com/piotrwitek/typesafe-actions/stargazers)\n\n\u003c!-- _Make a one time or a monthly donation to join [supporters](https://www.buymeacoffee.com/piotrekwitek)_ --\u003e\n\n\u003ca href=\"https://www.buymeacoffee.com/piotrekwitek\"\u003e\n  \u003cimg src=\"https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png\" alt=\"Buy Me a Coffee\"\u003e\n\u003c/a\u003e\n\n\u003c!-- _Or become a [sponsor](..) to get your logo with a link on our README_ --\u003e\n\n\u003ca href=\"https://www.patreon.com/piotrekwitek\"\u003e\n  \u003cimg src=\"https://c5.patreon.com/external/logo/become_a_patron_button@2x.png\" alt=\"Become a Patron\" width=\"160\"\u003e\n\u003c/a\u003e\n\n\u003cbr/\u003e\u003chr/\u003e\n\n### **What's new?**\n\n:tada: _Now updated to support **TypeScript v3.7**_ :tada:\n\n:warning: Library was recently updated to v5 :warning:\n\u003cbr/\u003e*Current API Docs and Tutorial are outdated (from v4), so temporarily please use this issue as [v5.x.x API Docs](https://github.com/piotrwitek/typesafe-actions/issues/143).*\n\n\u003chr/\u003e\u003cbr/\u003e\n\n\u003c/div\u003e\n\n### **Features**\n- Easily create completely typesafe [Actions](#action-creators-api) or even [Async Actions](#createasyncaction)\n- No boilerplate and completely typesafe [Reducers](#reducer-creators-api)\n- Game-changing [Helper Types](#type-helpers-api) for Redux\n\n### **Examples**\n\n- Todo-App playground: [Codesandbox](https://codesandbox.io/s/github/piotrwitek/typesafe-actions/tree/master/codesandbox)\n- React, Redux, TypeScript - RealWorld App: [Github](https://github.com/piotrwitek/react-redux-typescript-realworld-app) | [Demo](https://react-redux-typescript-realworld-app.netlify.com/)\n\n### **Goals**\n\n- **Secure and Minimal** - no third-party dependencies, according to `size-snapshot` (Minified: 3.48 KB, Gzipped: 1.03 KB), check also on [bundlephobia](https://bundlephobia.com/result?p=typesafe-actions)\n- **Optimized** - distribution packages bundled in 3 different formats (`cjs`, `esm` and `umd`) with separate bundles for dev \u0026 prod (same as `react`)\n- **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\n- **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:XXX`\n\n---\n\n## Table of Contents\n\n\u003c!-- START doctoc generated TOC please keep comment here to allow auto update --\u003e\n\u003c!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE --\u003e\n\n\n- [Installation](#installation)\n- [Tutorial v4 (v5 is WIP #188)](#tutorial-v4-v5-is-wip-188)\n  - [Constants](#constants)\n  - [Actions](#actions)\n    - [1. Basic actions](#1-basic-actions)\n    - [2. FSA compliant actions](#2-fsa-compliant-actions)\n    - [3. Custom actions (non-standard use-cases)](#3-custom-actions-non-standard-use-cases)\n  - [Action Helpers](#action-helpers)\n    - [Using action-creators instances instead of type-constants](#using-action-creators-instances-instead-of-type-constants)\n    - [Using regular type-constants](#using-regular-type-constants)\n  - [Reducers](#reducers)\n    - [Extending internal types to enable type-free syntax with `createReducer`](#extending-internal-types-to-enable-type-free-syntax-with-createreducer)\n    - [Using createReducer API with type-free syntax](#using-createreducer-api-with-type-free-syntax)\n    - [Alternative usage with regular switch reducer](#alternative-usage-with-regular-switch-reducer)\n  - [Async-Flows](#async-flows)\n    - [With `redux-observable` epics](#with-redux-observable-epics)\n    - [With `redux-saga` sagas](#with-redux-saga-sagas)\n- [API Docs v4 (v5 is WIP #189)](#api-docs-v4-v5-is-wip-189)\n  - [Action-Creators API](#action-creators-api)\n    - [`action`](#action)\n    - [`createAction`](#createaction)\n    - [`createStandardAction`](#createstandardaction)\n    - [`createCustomAction`](#createcustomaction)\n    - [`createAsyncAction`](#createasyncaction)\n  - [Reducer-Creators API](#reducer-creators-api)\n    - [`createReducer`](#createreducer)\n  - [Action-Helpers API](#action-helpers-api)\n    - [`getType`](#gettype)\n    - [`isActionOf`](#isactionof)\n    - [`isOfType`](#isoftype)\n  - [Type-Helpers API](#type-helpers-api)\n    - [`ActionType`](#actiontype)\n    - [`StateType`](#statetype)\n- [Migration Guides](#migration-guides)\n  - [`v4.x.x` to `v5.x.x`](#v4xx-to-v5xx)\n  - [`v3.x.x` to `v4.x.x`](#v3xx-to-v4xx)\n  - [`v2.x.x` to `v3.x.x`](#v2xx-to-v3xx)\n  - [`v1.x.x` to `v2.x.x`](#v1xx-to-v2xx)\n  - [Migrating from `redux-actions` to `typesafe-actions`](#migrating-from-redux-actions-to-typesafe-actions)\n- [Compatibility Notes](#compatibility-notes)\n- [Recipes](#recipes)\n  - [Restrict Meta type in `action` creator](#restrict-meta-type-in-action-creator)\n- [Compare to others](#compare-to-others)\n  - [`redux-actions`](#redux-actions)\n- [Motivation](#motivation)\n- [Contributing](#contributing)\n- [Funding Issues](#funding-issues)\n- [License](#license)\n\n\u003c!-- END doctoc generated TOC please keep comment here to allow auto update --\u003e\n\n\u003chr/\u003e\n\n## Installation\n\n```bash\n# NPM\nnpm install typesafe-actions\n\n# YARN\nyarn add typesafe-actions\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Tutorial v4 (v5 is WIP [#188](https://github.com/piotrwitek/typesafe-actions/issues/188))\n\nTo 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:\n\n\u003e **WARNING**  \n\u003e 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).\n\n[⇧ back to top](#table-of-contents)\n\n### Constants\n\n\u003e **RECOMMENDATION:**  \n\u003e 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.\n\n**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.\n\n```ts\n// Example file: './constants.ts'\n\n// WARNING: Incorrect usage\nexport const ADD = prefix + 'ADD'; // =\u003e string\nexport const ADD = `${prefix}/ADD`; // =\u003e string\nexport default {\n   ADD: '@prefix/ADD', // =\u003e string\n}\n\n// Correct usage\nexport const ADD = '@prefix/ADD'; // =\u003e '@prefix/ADD'\nexport const TOGGLE = '@prefix/TOGGLE'; // =\u003e '@prefix/TOGGLE'\nexport default ({\n  ADD: '@prefix/ADD', // =\u003e '@prefix/ADD'\n} as const) // working in TS v3.4 and above =\u003e https://github.com/Microsoft/TypeScript/pull/29510\n```\n\n[⇧ back to top](#table-of-contents)\n\n### Actions\n\nDifferent 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.\n\n#### 1. Basic actions\n`action` and `createAction` are creators that can create **actions** with predefined properties ({ type, payload, meta }). This makes them concise but also opinionated.\n \nImportant property is that resulting **action-creator** will have a variadic number of arguments and preserve their semantic names `(id, title, amount, etc...)`.\n\nThese two creators are very similar and the only real difference is that `action` **WILL NOT WORK** with **action-helpers**.\n\n```ts\nimport { action, createAction } from 'typesafe-actions';\n\nexport const add = (title: string) =\u003e action('todos/ADD', { id: cuid(), title, completed: false });\n// add: (title: string) =\u003e { type: \"todos/ADD\"; payload: { id: string, title: string, completed: boolean; }; }\n\nexport const add = createAction('todos/ADD', action =\u003e {\n  // Note: \"action\" callback does not need \"type\" parameter\n  return (title: string) =\u003e action({ id: cuid(), title, completed: false });\n});\n// add: (title: string) =\u003e { type: \"todos/ADD\"; payload: { id: string, title: string, completed: boolean; }; }\n```\n\n#### 2. FSA compliant actions\nThis 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.\n\nIt 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.\n\n\u003e **TIP**: This creator is the most compatible with `redux-actions` in case you are migrating.\n\n```ts\nimport { createStandardAction } from 'typesafe-actions';\n\nexport const toggle = createStandardAction('todos/TOGGLE')\u003cstring\u003e();\n// toggle: (payload: string) =\u003e { type: \"todos/TOGGLE\"; payload: string; }\n\nexport const add = createStandardAction('todos/ADD').map(\n  (title: string) =\u003e ({\n    payload: { id: cuid(), title, completed: false },\n  })\n);\n// add: (payload: string) =\u003e { type: \"todos/ADD\"; payload: { id: string, title: string, completed: boolean; }; }\n```\n\n#### 3. Custom actions (non-standard use-cases)\n\nThis approach will give us the most flexibility of all creators, providing a variadic number of named parameters and custom properties on **action** object to fit all the custom use-cases.\n\n```ts\nimport { createCustomAction } from 'typesafe-actions';\n\nconst add = createCustomAction('todos/ADD', type =\u003e {\n  return (title: string) =\u003e ({ type, id: cuid(), title, completed: false });\n});\n// add: (title: string) =\u003e { type: \"todos/ADD\"; id: string; title: string; completed: boolean; }\n```\n\n\u003e **TIP**: For more examples please check the [API Docs](#table-of-contents).\n\n\u003e **RECOMMENDATION**  \n\u003e Common approach is to create a `RootAction` in the central point of your redux store - it will represent all possible action types in your application. You can even merge it with third-party action types as shown below to make your model complete.\n\n```ts\n// types.d.ts\n// example of including `react-router` actions in `RootAction`\nimport { RouterAction, LocationChangeAction } from 'react-router-redux';\nimport { TodosAction } from '../features/todos';\n\ntype ReactRouterAction = RouterAction | LocationChangeAction;\n\nexport type RootAction =\n  | ReactRouterAction\n  | TodosAction;\n```\n\n[⇧ back to top](#table-of-contents)\n\n### Action Helpers\n\nNow I want to show you **action-helpers** and explain their use-cases. We're going to implement a side-effect responsible for showing a success toast when user adds a new todo.\n\nImportant thing to notice is that all these helpers are acting as a **type-guard** so they'll narrow **tagged union type** (`RootAction`) to a specific action type that we want.\n\n#### Using action-creators instances instead of type-constants\n\nInstead of **type-constants** we can use **action-creators** instance to match specific actions in reducers and epics cases. It works by adding a static property on **action-creator** instance which contains the `type` string. \n\nThe most common one is `getType`, which is useful for regular reducer switch cases:\n\n```ts\n  switch (action.type) {\n    case getType(todos.add):\n      // below action type is narrowed to: { type: \"todos/ADD\"; payload: Todo; }\n      return [...state, action.payload];\n    ...\n```\n\nThen we have the `isActionOf` helper which accept **action-creator** as first parameter matching actions with corresponding type passed as second parameter (it's a curried function).\n\n```ts\n// epics.ts\nimport { isActionOf } from 'typesafe-actions';\n\nimport { add } from './actions';\n\nconst addTodoToast: Epic\u003cRootAction, RootAction, RootState, Services\u003e = (action$, state$, { toastService }) =\u003e\n  action$.pipe(\n    filter(isActionOf(add)),\n    tap(action =\u003e { // here action type is narrowed to: { type: \"todos/ADD\"; payload: Todo; }\n      toastService.success(...);\n    })\n    ...\n    \n  // Works with multiple actions! (with type-safety up to 5)\n  action$.pipe(\n    filter(isActionOf([add, toggle])) // here action type is narrowed to a smaller union:\n    // { type: \"todos/ADD\"; payload: Todo; } | { type: \"todos/TOGGLE\"; payload: string; }\n```\n\n#### Using regular type-constants\nAlternatively if your team prefers to use regular **type-constants** you can still do that.\n\nWe have an equivalent helper (`isOfType`) which accept **type-constants** as parameter providing the same functionality.\n\n```ts\n// epics.ts\nimport { isOfType } from 'typesafe-actions';\n\nimport { ADD } from './constants';\n\nconst addTodoToast: Epic\u003cRootAction, RootAction, RootState, Services\u003e = (action$, state$, { toastService }) =\u003e\n  action$.pipe(\n    filter(isOfType(ADD)),\n    tap(action =\u003e { // here action type is narrowed to: { type: \"todos/ADD\"; payload: Todo; }\n    ...\n    \n  // Works with multiple actions! (with type-safety up to 5)\n  action$.pipe(\n    filter(isOfType([ADD, TOGGLE])) // here action type is narrowed to a smaller union:\n    // { type: \"todos/ADD\"; payload: Todo; } | { type: \"todos/TOGGLE\"; payload: string; }\n```\n\n\u003e **TIP:** you can use action-helpers with other types of conditional statements.\n\n```ts\nimport { isActionOf, isOfType } from 'typesafe-actions';\n\nif (isActionOf(actions.add, action)) {\n  // here action is narrowed to: { type: \"todos/ADD\"; payload: Todo; }\n}\n// or with type constants\nif (isOfType(types.ADD, action)) {\n  // here action is narrowed to: { type: \"todos/ADD\"; payload: Todo; }\n}\n```\n\n[⇧ back to top](#table-of-contents)\n\n### Reducers\n\n#### Extending internal types to enable type-free syntax with `createReducer`\n\nWe can extend internal types of `typesafe-actions` module with `RootAction` definition of our application so that you don't need to pass generic type arguments with `createReducer` API:\n\n```ts\n// types.d.ts\nimport { ActionType } from 'typesafe-actions';\n\nexport type RootAction = ActionType\u003ctypeof import('./actions').default\u003e;\n\ndeclare module 'typesafe-actions' {\n  interface Types {\n    RootAction: RootAction;\n  }\n}\n\n// now you can use\ncreateReducer(...)\n// instead of\ncreateReducer\u003cState, Action\u003e(...)\n```\n\n#### Using createReducer API with type-free syntax\n\nWe can prevent a lot of boilerplate code and type errors using this powerful and completely typesafe API.\n\nUsing handleAction chain API:\n```ts\n// using action-creators\nconst counterReducer = createReducer(0)\n  // state and action type is automatically inferred and return type is validated to be exact type\n  .handleAction(add, (state, action) =\u003e state + action.payload)\n  .handleAction(add, ... // \u003c= error is shown on duplicated or invalid actions\n  .handleAction(increment, (state, _) =\u003e state + 1)\n  .handleAction(... // \u003c= error is shown when all actions are handled\n  \n  // or handle multiple actions using array\n  .handleAction([add, increment], (state, action) =\u003e\n    state + (action.type === 'ADD' ? action.payload : 1)\n  );\n\n// all the same scenarios are working when using type-constants\nconst counterReducer = createReducer(0)\n  .handleAction('ADD', (state, action) =\u003e state + action.payload)\n  .handleAction('INCREMENT', (state, _) =\u003e state + 1);\n  \ncounterReducer(0, add(4)); // =\u003e 4\ncounterReducer(0, increment()); // =\u003e 1\n```\n\n#### Alternative usage with regular switch reducer\n\nFirst we need to start by generating a **tagged union type** of actions (`TodosAction`). It's very easy to do by using `ActionType` **type-helper**.\n\n```ts\nimport { ActionType } from 'typesafe-actions';\n\nimport * as todos from './actions';\nexport type TodosAction = ActionType\u003ctypeof todos\u003e;\n```\n\nNow we define a regular reducer function by annotating `state` and `action` arguments with their respective types (`TodosAction` for action type).\n\n```ts\nexport default (state: Todo[] = [], action: TodosAction) =\u003e {\n```\n\nNow in the switch cases we can use the `type` property of action to narrowing the union type of `TodosAction` to an action that is corresponding to that type.\n\n```ts\n  switch (action.type) {\n    case getType(add):\n      // below action type is narrowed to: { type: \"todos/ADD\"; payload: Todo; }\n      return [...state, action.payload];\n    ...\n```\n\n[⇧ back to top](#table-of-contents)\n\n### Async-Flows\n\n#### With `redux-observable` epics\n\nTo handle an async-flow of http request lets implement an `epic`. The `epic` will call a remote API using an injected `todosApi` client, which will return a Promise that we'll need to handle by using three different actions that correspond to triggering, success and failure.\n\nTo help us simplify the creation process of necessary action-creators, we'll use `createAsyncAction` function providing us with a nice common interface object `{ request: ... , success: ... , failure: ... }` that will nicely fit with the functional API of `RxJS`.\nThis will mitigate **redux verbosity** and greatly reduce the maintenance cost of type annotations for **actions** and **action-creators** that would otherwise be written explicitly.\n\n```ts\n// actions.ts\nimport { createAsyncAction } from 'typesafe-actions';\n\nconst fetchTodosAsync = createAsyncAction(\n  'FETCH_TODOS_REQUEST',\n  'FETCH_TODOS_SUCCESS',\n  'FETCH_TODOS_FAILURE',\n  'FETCH_TODOS_CANCEL'\n)\u003cstring, Todo[], Error, string\u003e();\n\n// epics.ts\nimport { fetchTodosAsync } from './actions';\n\nconst fetchTodosFlow: Epic\u003cRootAction, RootAction, RootState, Services\u003e = (action$, state$, { todosApi }) =\u003e\n  action$.pipe(\n    filter(isActionOf(fetchTodosAsync.request)),\n    switchMap(action =\u003e\n      from(todosApi.getAll(action.payload)).pipe(\n        map(fetchTodosAsync.success),\n        catchError((message: string) =\u003e of(fetchTodosAsync.failure(message))),\n        takeUntil(action$.pipe(filter(isActionOf(fetchTodosAsync.cancel)))),\n      )\n    )\n  );\n```\n\n#### With `redux-saga` sagas\nWith sagas it's not possible to achieve the same degree of type-safety as with epics because of limitations coming from `redux-saga` API design.\n\nTypescript issues:\n- [Typescript does not currently infer types resulting from a `yield` statement](https://github.com/Microsoft/TypeScript/issues/2983) so you have to manually assert the type  e.g. `const response: Todo[] = yield call(...`\n\n*Here is the latest recommendation although it's not fully optimal. If you managed to cook something better, please open an issue to share your finding with us.*\n\n```ts\nimport { createAsyncAction, createReducer } from 'typesafe-actions';\nimport { put, call, takeEvery, all } from 'redux-saga/effects';\n\n// Create the set of async actions\nconst fetchTodosAsync = createAsyncAction(\n  'FETCH_TODOS_REQUEST',\n  'FETCH_TODOS_SUCCESS',\n  'FETCH_TODOS_FAILURE'\n)\u003cstring, Todo[], Error\u003e();\n\n// Handle request saga\nfunction* addTodoSaga(action: ReturnType\u003ctypeof fetchTodosAsync.request\u003e): Generator {\n  try {\n    const response: Todo[] = yield call(todosApi.getAll, action.payload);\n\n    yield put(fetchTodosAsync.success(response));\n  } catch (err) {\n    yield put(fetchTodosAsync.failure(err));\n  }\n}\n\n// Main saga\nfunction* mainSaga() {\n    yield all([\n        takeEvery(fetchTodosAsync.request, addTodoSaga),\n    ]);\n}\n\n// Handle success reducer\nexport const todoReducer = createReducer({})\n    .handleAction(fetchTodosAsync.success, (state, action) =\u003e ({ ...state, todos: action.payload }));\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## API Docs v4 (v5 is WIP [#189](https://github.com/piotrwitek/typesafe-actions/issues/189))\n\n### Action-Creators API\n\n#### `action`\n\n_Simple **action factory function** to simplify creation of type-safe actions._\n\n\u003e **WARNING**:  \n\u003e This approach will **NOT WORK** with **action-helpers** (such as `getType` and `isActionOf`) because it is creating **action objects** while all the other creator functions are returning **enhanced action-creators**.\n\n```ts\naction(type, payload?, meta?, error?)\n```\n\nExamples:\n[\u003e Advanced Usage Examples](src/action.spec.ts)\n\n```ts\nconst increment = () =\u003e action('INCREMENT');\n// { type: 'INCREMENT'; }\n\nconst createUser = (id: number, name: string) =\u003e\n  action('CREATE_USER', { id, name });\n// { type: 'CREATE_USER'; payload: { id: number; name: string }; }\n\nconst getUsers = (params?: string) =\u003e\n  action('GET_USERS', undefined, params);\n// { type: 'GET_USERS'; meta: string | undefined; }\n```\n\n\u003e **TIP**: Starting from TypeScript v3.4 you can achieve similar results using new `as const` operator.\n\n```ts\nconst increment = () =\u003e ({ type: 'INCREMENT' } as const);\n```\n\n#### `createAction`\n\n_Create an enhanced action-creator with unlimited number of arguments._\n- Resulting action-creator will preserve semantic names of their arguments  `(id, title, amount, etc...)`.\n- Returned action object have predefined properties `({ type, payload, meta })`\n\n```ts\ncreateAction(type)\ncreateAction(type, actionCallback =\u003e {\n  return (namedArg1, namedArg2, ...namedArgN) =\u003e actionCallback(payload?, meta?)\n})\n```\n\u003e **TIP**: Injected `actionCallback` argument is similar to `action` API but doesn't need the \"type\" parameter\n\nExamples:\n[\u003e Advanced Usage Examples](src/create-action.spec.ts)\n\n```ts\nimport { createAction } from 'typesafe-actions';\n\n// - with type only\nconst increment = createAction('INCREMENT');\ndispatch(increment());\n// { type: 'INCREMENT' };\n\n// - with type and payload\nconst add = createAction('ADD', action =\u003e {\n  return (amount: number) =\u003e action(amount);\n});\ndispatch(add(10));\n// { type: 'ADD', payload: number }\n\n// - with type and meta\nconst getTodos = createAction('GET_TODOS', action =\u003e {\n  return (params: Params) =\u003e action(undefined, params);\n});\ndispatch(getTodos('some_meta'));\n// { type: 'GET_TODOS', meta: Params }\n\n// - and finally with type, payload and meta\nconst getTodo = createAction('GET_TODO', action =\u003e {\n  return (id: string, meta: string) =\u003e action(id, meta);\n});\ndispatch(getTodo('some_id', 'some_meta'));\n// { type: 'GET_TODO', payload: string, meta: string }\n```\n\n[⇧ back to top](#table-of-contents)\n\n#### `createStandardAction`\n\n_Create an enhanced action-creator compatible with [Flux Standard Action](https://github.com/redux-utilities/flux-standard-action) to reduce boilerplate and enforce convention._\n- Resulting action-creator have predefined arguments `(payload, meta)`\n- Returned action object have predefined properties `({ type, payload, meta, error })`\n- But it also contains a `.map()` method that allow to map `(payload, meta)` arguments to a custom action object `({ customProp1, customProp2, ...customPropN })`\n\n```ts\ncreateStandardAction(type)()\ncreateStandardAction(type)\u003cTPayload, TMeta?\u003e()\ncreateStandardAction(type).map((payload, meta) =\u003e ({ customProp1, customProp2, ...customPropN }))\n```\n\n\u003e **TIP**: Using `undefined` as generic type parameter you can make the action-creator function require NO parameters.\n\nExamples:\n[\u003e Advanced Usage Examples](src/create-standard-action.spec.ts)\n\n```ts\nimport { createStandardAction } from 'typesafe-actions';\n\n// Very concise with use of generic type arguments\n// - with type only\nconst increment = createStandardAction('INCREMENT')();\nconst increment = createStandardAction('INCREMENT')\u003cundefined\u003e();\nincrement(); // { type: 'INCREMENT' } (no parameters are required)\n\n\n// - with type and payload\nconst add = createStandardAction('ADD')\u003cnumber\u003e();\nadd(10); // { type: 'ADD', payload: number }\n\n// - with type and meta\nconst getData = createStandardAction('GET_DATA')\u003cundefined, string\u003e();\ngetData(undefined, 'meta'); // { type: 'GET_DATA', meta: string }\n\n// - and finally with type, payload and meta\nconst getData = createStandardAction('GET_DATA')\u003cnumber, string\u003e();\ngetData(1, 'meta'); // { type: 'GET_DATA', payload: number, meta: string }\n\n// Can map payload and meta arguments to a custom action object\nconst notify = createStandardAction('NOTIFY').map(\n  (payload: string, meta: Meta) =\u003e ({\n    from: meta.username,\n    message: `${meta.username}: ${payload}`,\n    messageType: meta.type,\n    datetime: new Date(),\n  })\n);\n\ndispatch(notify('Hello!', { username: 'Piotr', type: 'announcement' }));\n// { type: 'NOTIFY', from: string, message: string, messageType: MessageType, datetime: Date }\n```\n\n[⇧ back to top](#table-of-contents)\n\n#### `createCustomAction`\n\n_Create an enhanced action-creator with unlimited number of arguments and custom properties on action object._\n- Resulting action-creator will preserve semantic names of their arguments  `(id, title, amount, etc...)`.\n- Returned action object have custom properties `({ type, customProp1, customProp2, ...customPropN })`\n\n```ts\ncreateCustomAction(type, type =\u003e {\n  return (namedArg1, namedArg2, ...namedArgN) =\u003e ({ type, customProp1, customProp2, ...customPropN })\n})\n```\n\nExamples:\n[\u003e Advanced Usage Examples](src/create-custom-action.spec.ts)\n\n```ts\nimport { createCustomAction } from 'typesafe-actions';\n\nconst add = createCustomAction('CUSTOM', type =\u003e {\n  return (first: number, second: number) =\u003e ({ type, customProp1: first, customProp2: second });\n});\n\ndispatch(add(1));\n// { type: \"CUSTOM\"; customProp1: number; customProp2: number; }\n```\n\n[⇧ back to top](#table-of-contents)\n\n#### `createAsyncAction`\n\n_Create an object containing three enhanced action-creators to simplify handling of async flows (e.g. network request - request/success/failure)._\n\n```ts\ncreateAsyncAction(\n  requestType, successType, failureType, cancelType?\n)\u003cTRequestPayload, TSuccessPayload, TFailurePayload, TCancelPayload?\u003e()\n```\n\n##### `AsyncActionCreator`\n\n```ts\ntype AsyncActionCreator\u003c\n  [TRequestType, TRequestPayload],\n  [TSuccessType, TSuccessPayload],\n  [TFailureType, TFailurePayload],\n  [TCancelType, TCancelPayload]?\n\u003e = {\n  request: StandardActionCreator\u003cTRequestType, TRequestPayload\u003e,\n  success: StandardActionCreator\u003cTSuccessType, TSuccessPayload\u003e,\n  failure: StandardActionCreator\u003cTFailureType, TFailurePayload\u003e,\n  cancel?: StandardActionCreator\u003cTCancelType, TCancelPayload\u003e,\n}\n```\n\n\u003e **TIP**: Using `undefined` as generic type parameter you can make the action-creator function require NO parameters.\n\nExamples:\n[\u003e Advanced Usage Examples](src/create-async-action.spec.ts)\n\n```ts\nimport { createAsyncAction, AsyncActionCreator } from 'typesafe-actions';\n\nconst fetchUsersAsync = createAsyncAction(\n  'FETCH_USERS_REQUEST',\n  'FETCH_USERS_SUCCESS',\n  'FETCH_USERS_FAILURE'\n)\u003cstring, User[], Error\u003e();\n\ndispatch(fetchUsersAsync.request(params));\n\ndispatch(fetchUsersAsync.success(response));\n\ndispatch(fetchUsersAsync.failure(err));\n\nconst fn = (\n  a: AsyncActionCreator\u003c\n    ['FETCH_USERS_REQUEST', string],\n    ['FETCH_USERS_SUCCESS', User[]],\n    ['FETCH_USERS_FAILURE', Error]\n  \u003e\n) =\u003e a;\nfn(fetchUsersAsync);\n\n// There is 4th optional argument to declare cancel action\nconst fetchUsersAsync = createAsyncAction(\n  'FETCH_USERS_REQUEST',\n  'FETCH_USERS_SUCCESS',\n  'FETCH_USERS_FAILURE'\n  'FETCH_USERS_CANCEL'\n)\u003cstring, User[], Error, string\u003e();\n\ndispatch(fetchUsersAsync.cancel('reason'));\n\nconst fn = (\n  a: AsyncActionCreator\u003c\n    ['FETCH_USERS_REQUEST', string],\n    ['FETCH_USERS_SUCCESS', User[]],\n    ['FETCH_USERS_FAILURE', Error],\n    ['FETCH_USERS_CANCEL', string]\n  \u003e\n) =\u003e a;\nfn(fetchUsersAsync);\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n### Reducer-Creators API\n\n#### `createReducer`\n\n_Create a typesafe reducer_\n\n```ts\ncreateReducer\u003cTState, TRootAction\u003e(initialState, handlersMap?)\n// or\ncreateReducer\u003cTState, TRootAction\u003e(initialState)\n  .handleAction(actionCreator, reducer)\n  .handleAction([actionCreator1, actionCreator2, ...actionCreatorN], reducer)\n  .handleType(type, reducer)\n  .handleType([type1, type2, ...typeN], reducer)\n```\n\nExamples:\n[\u003e Advanced Usage Examples](src/create-reducer.spec.ts)\n\n\u003e **TIP:** You can use reducer API with a **type-free** syntax by [Extending internal types](#extending-internal-types-to-enable-type-free-syntax-with-createreducer), otherwise you'll have to pass generic type arguments like in below examples\n```ts\n// type-free syntax doesn't require generic type arguments\nconst counterReducer = createReducer(0, { \n  ADD: (state, action) =\u003e state + action.payload,\n  [getType(increment)]: (state, _) =\u003e state + 1,\n})\n```\n\n**Object map style:**\n```ts\nimport { createReducer, getType } from 'typesafe-actions'\n\ntype State = number;\ntype Action = { type: 'ADD', payload: number } | { type: 'INCREMENT' };\n\nconst counterReducer = createReducer\u003cState, Action\u003e(0, { \n  ADD: (state, action) =\u003e state + action.payload,\n  [getType(increment)]: (state, _) =\u003e state + 1,\n})\n```\n\n**Chain API style:**\n```ts\n// using action-creators\nconst counterReducer = createReducer\u003cState, Action\u003e(0)\n  .handleAction(add, (state, action) =\u003e state + action.payload)\n  .handleAction(increment, (state, _) =\u003e state + 1)\n\n  // handle multiple actions by using array\n  .handleAction([add, increment], (state, action) =\u003e\n    state + (action.type === 'ADD' ? action.payload : 1)\n  );\n\n// all the same scenarios are working when using type-constants\nconst counterReducer = createReducer\u003cState, Action\u003e(0)\n  .handleType('ADD', (state, action) =\u003e state + action.payload)\n  .handleType('INCREMENT', (state, _) =\u003e state + 1);\n```\n\n**Extend or compose reducers - every operation is completely typesafe:**\n```ts\nconst newCounterReducer = createReducer\u003cState, Action\u003e(0)\n  .handleAction('SUBTRACT', (state, action) =\u003e state - action.payload)\n  .handleAction('DECREMENT', (state, _) =\u003e state - 1);\n\nconst bigReducer = createReducer\u003cState, Action\u003e(0, {\n  ...counterReducer.handlers, // typesafe\n  ...newCounterReducer.handlers, // typesafe\n  SUBTRACT: decrementReducer.handlers.DECREMENT, // \u003c= error, wrong type\n})\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n### Action-Helpers API\n\n#### `getType`\n\n_Get the **type** property value (narrowed to literal type) of given enhanced action-creator._\n\n```ts\ngetType(actionCreator)\n```\n\n[\u003e Advanced Usage Examples](src/get-type.spec.ts)\n\nExamples:\n```ts\nimport { getType, createStandardAction } from 'typesafe-actions';\n\nconst add = createStandardAction('ADD')\u003cnumber\u003e();\n\n// In switch reducer\nswitch (action.type) {\n  case getType(add):\n    // action type is { type: \"ADD\"; payload: number; }\n    return state + action.payload;\n\n  default:\n    return state;\n}\n\n// or with conditional statements\nif (action.type === getType(add)) {\n  // action type is { type: \"ADD\"; payload: number; }\n}\n```\n\n[⇧ back to top](#table-of-contents)\n\n#### `isActionOf`\n\n_Check if action is an instance of given enhanced action-creator(s)\n(it will narrow action type to a type of given action-creator(s))_\n\n\n\u003e **WARNING**: Regular action creators and [action](#action) will not work with this helper\n\n```ts\n// can be used as a binary function\nisActionOf(actionCreator, action)\n// or as a curried function\nisActionOf(actionCreator)(action)\n// also accepts an array\nisActionOf([actionCreator1, actionCreator2, ...actionCreatorN], action)\n// with its curried equivalent\nisActionOf([actionCreator1, actionCreator2, ...actionCreatorN])(action)\n```\n\nExamples:\n[\u003e Advanced Usage Examples](src/is-action-of.spec.ts)\n\n```ts\nimport { addTodo, removeTodo } from './todos-actions';\n\n// Works with any filter type function (`Array.prototype.filter`, lodash, ramda, rxjs, etc.)\n// - single action\n[action1, action2, ...actionN]\n  .filter(isActionOf(addTodo)) // only actions with type `ADD` will pass\n  .map((action) =\u003e {\n    // action type is { type: \"todos/ADD\"; payload: Todo; }\n    ...\n    \n// - multiple actions\n[action1, action2, ...actionN]\n  .filter(isActionOf([addTodo, removeTodo])) // only actions with type `ADD` or 'REMOVE' will pass\n  .do((action) =\u003e {\n    // action type is { type: \"todos/ADD\"; payload: Todo; } | { type: \"todos/REMOVE\"; payload: Todo; }\n    ...\n      \n// With conditional statements\n// - single action\nif(isActionOf(addTodo, action)) {\n  return iAcceptOnlyTodoType(action.payload);\n  // action type is { type: \"todos/ADD\"; payload: Todo; }\n}\n// - multiple actions\nif(isActionOf([addTodo, removeTodo], action)) {\n  return iAcceptOnlyTodoType(action.payload);\n  // action type is { type: \"todos/ADD\"; payload: Todo; } | { type: \"todos/REMOVE\"; payload: Todo; }\n}\n```\n\n[⇧ back to top](#table-of-contents)\n\n#### `isOfType`\n\n_Check if action type property is equal given type-constant(s)\n(it will narrow action type to a type of given action-creator(s))_\n\n```ts\n// can be used as a binary function\nisOfType(type, action)\n// or as curried function\nisOfType(type)(action)\n// also accepts an array\nisOfType([type1, type2, ...typeN], action)\n// with its curried equivalent\nisOfType([type1, type2, ...typeN])(action)\n```\n\nExamples:\n[\u003e Advanced Usage Examples](src/is-of-type.spec.ts)\n\n```ts\nimport { ADD, REMOVE } from './todos-types';\n\n// Works with any filter type function (`Array.prototype.filter`, lodash, ramda, rxjs, etc.)\n// - single action\n[action1, action2, ...actionN]\n  .filter(isOfType(ADD)) // only actions with type `ADD` will pass\n  .map((action) =\u003e {\n    // action type is { type: \"todos/ADD\"; payload: Todo; }\n    ...\n    \n// - multiple actions\n[action1, action2, ...actionN]\n  .filter(isOfType([ADD, REMOVE])) // only actions with type `ADD` or 'REMOVE' will pass\n  .do((action) =\u003e {\n    // action type is { type: \"todos/ADD\"; payload: Todo; } | { type: \"todos/REMOVE\"; payload: Todo; }\n    ...\n      \n// With conditional statements\n// - single action\nif(isOfType(ADD, action)) {\n  return iAcceptOnlyTodoType(action.payload);\n  // action type is { type: \"todos/ADD\"; payload: Todo; }\n}\n// - multiple actions\nif(isOfType([ADD, REMOVE], action)) {\n  return iAcceptOnlyTodoType(action.payload);\n  // action type is { type: \"todos/ADD\"; payload: Todo; } | { type: \"todos/REMOVE\"; payload: Todo; }\n}\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n### Type-Helpers API\nBelow helper functions are very flexible generalizations, works great with nested structures and will cover numerous different use-cases.\n\n#### `ActionType`\n\n_Powerful type-helper that will infer union type from **import * as ...** or **action-creator map** object._\n\n```ts\nimport { ActionType } from 'typesafe-actions';\n\n// with \"import * as ...\"\nimport * as todos from './actions';\nexport type TodosAction = ActionType\u003ctypeof todos\u003e;\n// TodosAction: { type: 'action1' } | { type: 'action2' } | { type: 'action3' }\n\n// with nested action-creator map case\nconst actions = {\n  action1: createAction('action1'),\n  nested: {\n    action2: createAction('action2'),\n    moreNested: {\n      action3: createAction('action3'),\n    },\n  },\n};\nexport type RootAction = ActionType\u003ctypeof actions\u003e;\n// RootAction: { type: 'action1' } | { type: 'action2' } | { type: 'action3' }\n```\n\n[⇧ back to top](#table-of-contents)\n\n#### `StateType`\n\n_Powerful type helper that will infer state object type from **reducer function** and **nested/combined reducers**._\n\n\u003e **WARNING**: working with redux@4+ types\n\n```ts\nimport { combineReducers } from 'redux';\nimport { StateType } from 'typesafe-actions';\n\n// with reducer function\nconst todosReducer = (state: Todo[] = [], action: TodosAction) =\u003e {\n  switch (action.type) {\n    case getType(todos.add):\n      return [...state, action.payload];\n    ...\nexport type TodosState = StateType\u003ctypeof todosReducer\u003e;\n\n// with nested/combined reducers\nconst rootReducer = combineReducers({\n  router: routerReducer,\n  counters: countersReducer,\n});\nexport type RootState = StateType\u003ctypeof rootReducer\u003e;\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Migration Guides\n\n### `v4.x.x` to `v5.x.x`\n\n**Breaking changes:**\n\n1. In `v5` all the deprecated `v4` creator functions are available under `deprecated` named import to help with incremental migration.\n```ts\n// before\nimport { createAction, createStandardAction, createCustomAction } from \"typesafe-actions\"\n\n// after\nimport { deprecated } from \"typesafe-actions\"\nconst { createAction, createStandardAction, createCustomAction } = deprecated;\n```\n\n2. `createStandardAction` was renamed to `createAction` and `.map` method was removed in favor of simpler `redux-actions` style API.\n```ts\n// before\nconst withMappedPayloadAndMeta = createStandardAction(\n  'CREATE_STANDARD_ACTION'\n).map(({ username, message }: Notification) =\u003e ({\n  payload: `${username}: ${message}`,\n  meta: { username, message },\n}));\n\n// after\nconst withMappedPayloadAndMeta = createAction(\n  'CREATE_STANDARD_ACTION',\n  ({ username, message }: Notification) =\u003e `${username}: ${message}`, // payload creator\n  ({ username, message }: Notification) =\u003e ({ username, message }) // meta creator\n)();\n```\n\n3. `v4` version of `createAction` was removed. I suggest to refactor to use a new `createAction` as in point `2`, which was simplified and extended to support `redux-actions` style API.\n```ts\n// before\nconst withPayloadAndMeta = createAction('CREATE_ACTION', resolve =\u003e {\n  return (id: number, token: string) =\u003e resolve(id, token);\n});\n\n// after\nconst withPayloadAndMeta = createAction(\n  'CREATE_ACTION',\n  (id: number, token: string) =\u003e id, // payload creator\n  (id: number, token: string) =\u003e token // meta creator\n})();\n```\n\n4. `createCustomAction` - API was greatly simplified, now it's used like this:\n```ts\n// before\nconst add = createCustomAction('CUSTOM', type =\u003e {\n  return (first: number, second: number) =\u003e ({ type, customProp1: first, customProp2: second });\n});\n\n// after\nconst add = createCustomAction(\n  'CUSTOM',\n  (first: number, second: number) =\u003e ({ customProp1: first, customProp2: second })\n);\n```\n\n5. `AsyncActionCreator` should be just renamed to `AsyncActionCreatorBuilder`.\n```ts\n// before\nimport { AsyncActionCreator } from \"typesafe-actions\"\n\n//after\nimport { AsyncActionCreatorBuilder } from \"typesafe-actions\"\n```\n\n### `v3.x.x` to `v4.x.x`\n\n**No breaking changes!**\n\n### `v2.x.x` to `v3.x.x`\n\nMinimal supported TypeScript `v3.1+`.\n\n### `v1.x.x` to `v2.x.x`\n\n**Breaking changes:**\n\n1. `createAction`\n- In `v2` we provide a `createActionDeprecated` function compatible with `v1` `createAction` to help with incremental migration.\n\n```ts\n// in v1 we created action-creator like this:\nconst getTodo = createAction('GET_TODO',\n  (id: string, meta: string) =\u003e ({\n    type: 'GET_TODO',\n    payload: id,\n    meta: meta,\n  })\n);\n\ngetTodo('some_id', 'some_meta'); // { type: 'GET_TODO', payload: 'some_id', meta: 'some_meta' }\n\n// in v2 we offer few different options - please choose your preference\nconst getTodoNoHelpers = (id: string, meta: string) =\u003e action('GET_TODO', id, meta);\n\nconst getTodoWithHelpers = createAction('GET_TODO', action =\u003e {\n  return (id: string, meta: string) =\u003e action(id, meta);\n});\n\nconst getTodoFSA = createStandardAction('GET_TODO')\u003cstring, string\u003e();\n\nconst getTodoCustom = createStandardAction('GET_TODO').map(\n  ({ id, meta }: { id: string; meta: string; }) =\u003e ({\n    payload: id,\n    meta,\n  })\n);\n```\n\n[⇧ back to top](#table-of-contents)\n\n### Migrating from `redux-actions` to `typesafe-actions`\n\n- createAction(s)\n\n```ts\ncreateAction(type, payloadCreator, metaCreator) =\u003e createStandardAction(type)() || createStandardAction(type).map(payloadMetaCreator)\n\ncreateActions() =\u003e // COMING SOON!\n```\n\n- handleAction(s)\n\n```ts\nhandleAction(type, reducer, initialState) =\u003e createReducer(initialState).handleAction(type, reducer)\n\nhandleActions(reducerMap, initialState) =\u003e createReducer(initialState, reducerMap)\n```\n\n\u003e TIP: If migrating from JS -\u003e TS, you can swap out action-creators from `redux-actions` with action-creators from `typesafe-actions` in your `handleActions` handlers. This works because the action-creators from `typesafe-actions` provide the same `toString` method implementation used by `redux-actions` to match actions to the correct reducer.\n\n- combineActions\n\nNot needed because each function in the API accept single value or array of values for action types or action creators.\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Compatibility Notes\n\n**TypeScript support**\n\n- `5.X.X` - TypeScript v3.2+\n- `4.X.X` - TypeScript v3.2+\n- `3.X.X` - TypeScript v3.2+\n- `2.X.X` - TypeScript v2.9+\n- `1.X.X` - TypeScript v2.7+\n\n**Browser support**\n\nIt's compatible with all modern browsers.\n\nFor older browsers support (e.g. IE \u003c= 11) and some mobile devices you need to provide the following polyfills:\n- [Object.assign](https://developer.mozilla.org/pl/docs/Web/JavaScript/Referencje/Obiekty/Object/assign#Polyfill)\n- [Array.prototype.includes](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes)\n\n**Recommended polyfill for IE**\n\nTo 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`.\nPlease check the `React` guidelines on how to do that: [LINK](https://reactjs.org/docs/javascript-environment-requirements.html)\nA polyfill fo IE11 is included in our `/codesandbox` application.\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Recipes\n\n### Restrict Meta type in `action` creator\nUsing this recipe you can create an action creator with restricted Meta type with exact object shape.\n\n```tsx\nexport type MetaType = {\n  analytics?: {\n    eventName: string;\n  };\n};\n\nexport const actionWithRestrictedMeta = \u003cT extends string, P\u003e(\n  type: T,\n  payload: P,\n  meta: MetaType\n) =\u003e action(type, payload, meta);\n\nexport const validAction = (payload: string) =\u003e\n  actionWithRestrictedMeta('type', payload, { analytics: { eventName: 'success' } }); // OK!\n\nexport const invalidAction = (payload: string) =\u003e\n  actionWithRestrictedMeta('type', payload, { analytics: { excessProp: 'no way!' } }); // Error\n// Object literal may only specify known properties, and 'excessProp' does not exist in type '{ eventName: string; }\n```\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Compare to others\n\nHere you can find out a detailed comparison of `typesafe-actions` to other solutions.\n\n### `redux-actions`\nLets compare the 3 most common variants of action-creators (with type only, with payload and with payload + meta)\n\nNote: tested with \"@types/redux-actions\": \"2.2.3\"\n\n**- with type only (no payload)**\n\n##### redux-actions\n```ts\nconst notify1 = createAction('NOTIFY');\n// resulting type:\n// () =\u003e {\n//   type: string;\n//   payload: void | undefined;\n//   error: boolean | undefined;\n// }\n```\n\n\u003e with `redux-actions` you can notice the redundant nullable `payload` property and literal type of `type` property is lost (discrimination of union type would not be possible)\n\n##### typesafe-actions\n```ts\nconst notify1 = () =\u003e action('NOTIFY');\n// resulting type:\n// () =\u003e {\n//   type: \"NOTIFY\";\n// }\n```\n\n\u003e with `typesafe-actions` there is no excess nullable types and no excess properties and the action \"type\" property is containing a literal type\n\n**- with payload**\n\n##### redux-actions\n```ts\nconst notify2 = createAction('NOTIFY',\n  (username: string, message?: string) =\u003e ({\n    message: `${username}: ${message || 'Empty!'}`,\n  })\n);\n// resulting type:\n// (t1: string) =\u003e {\n//   type: string;\n//   payload: { message: string; } | undefined;\n//   error: boolean | undefined;\n// }\n```\n\n\u003e first the optional `message` parameter is lost, `username` semantic argument name is changed to some generic `t1`, `type` property is widened once again and `payload` is nullable because of broken inference\n\n##### typesafe-actions\n```ts\nconst notify2 = (username: string, message?: string) =\u003e action(\n  'NOTIFY',\n  { message: `${username}: ${message || 'Empty!'}` },\n);\n// resulting type:\n// (username: string, message?: string | undefined) =\u003e {\n//   type: \"NOTIFY\";\n//   payload: { message: string; };\n// }\n```\n\n\u003e `typesafe-actions` infer very precise resulting type, notice working optional parameters and semantic argument names are preserved which is really important for great intellisense experience\n\n**- with payload and meta**\n\n##### redux-actions\n```ts\nconst notify3 = createAction('NOTIFY',\n  (username: string, message?: string) =\u003e (\n    { message: `${username}: ${message || 'Empty!'}` }\n  ),\n  (username: string, message?: string) =\u003e (\n    { username, message }\n  )\n);\n// resulting type:\n// (...args: any[]) =\u003e {\n//   type: string;\n//   payload: { message: string; } | undefined;\n//   meta: { username: string; message: string | undefined; };\n//   error: boolean | undefined;\n// }\n```\n\n\u003e this time we got a completely broken arguments arity with no type-safety because of `any` type with all the earlier issues\n\n##### typesafe-actions\n```ts\n/**\n * typesafe-actions\n */\nconst notify3 = (username: string, message?: string) =\u003e action(\n  'NOTIFY',\n  { message: `${username}: ${message || 'Empty!'}` },\n  { username, message },\n);\n// resulting type:\n// (username: string, message?: string | undefined) =\u003e {\n//   type: \"NOTIFY\";\n//   payload: { message: string; };\n//   meta: { username: string; message: string | undefined; };\n// }\n```\n\n\u003e `typesafe-actions` never fail to `any` type, even with this advanced scenario all types are correct and provide complete type-safety and excellent developer experience \n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Motivation\n\nWhen 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).\n\nExisting 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 😱)\n\n**So I created `typesafe-actions` to address all of the above pain points.**\n\nThe 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 will create additional noise in your code.\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Contributing\n\nYou can help make this project better by contributing. If you're planning to contribute please make sure to check our contributing guide: [CONTRIBUTING.md](/CONTRIBUTING.md)\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## Funding Issues\n\nYou can also help by funding issues.\nIssues like bug fixes or feature requests can be very quickly resolved when funded through the IssueHunt platform.\n\nI highly recommend to add a bounty to the issue that you're waiting for to increase priority and attract contributors willing to work on it.\n\n[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/110746954)\n\n[⇧ back to top](#table-of-contents)\n\n---\n\n## License\n\n[MIT License](/LICENSE)\n\nCopyright (c) 2017 Piotr Witek \u003cpiotrek.witek@gmail.com\u003e (http://piotrwitek.github.io)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrwitek%2Ftypesafe-actions","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpiotrwitek%2Ftypesafe-actions","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpiotrwitek%2Ftypesafe-actions/lists"}