typed-redux-saga

Redux-Saga effects with strong types.

Usage no npm install needed!

<script type="module">
  import typedReduxSaga from 'https://cdn.skypack.dev/typed-redux-saga';
</script>

README

Typed Redux Saga

npm Build Status Type Coverage codecov

dependencies Status devDependencies Status peerDependencies Status

An attempt to bring better TypeScript typing to redux-saga.

Requires TypeScript 3.6 or later.

Installation

# yarn
yarn add typed-redux-saga

# npm
npm install typed-redux-saga

Usage

Let's take the example from https://redux-saga.js.org/#sagasjs

Before

import { call, all } from "redux-saga/effects";
// Let's assume Api.fetchUser() returns Promise<User>
// Api.fetchConfig1/fetchConfig2 returns Promise<Config1>, Promise<Config2>
import Api from "...";

function* fetchUser(action) {
  // `user` has type any
  const user = yield call(Api.fetchUser, action.payload.userId);
  ...
}

function* fetchConfig() {}
  // `result` has type any
  const result = yield all({
    api1: call(Api.fetchConfig1),
    api2: call(Api.fetchConfig2),
  });
  ...
}

After

// Note we import `call` from typed-redux-saga
import { call, all } from "typed-redux-saga";
// Let's assume Api.fetchUser() returns Promise<User>
// Api.fetchConfig1/fetchConfig2 returns Promise<Config1>, Promise<Config2>
import Api from "...";

function* fetchUser(action) {
  // Note yield is replaced with yield*
  // `user` now has type User, not any!
  const user = yield* call(Api.fetchUser, action.payload.userId);
  ...
}

function* fetchConfig() {}
  // Note yield is replaced with yield*
  // `result` now has type {api1: Config1, api2: Config2}
  const result = yield* all({
    api1: call(Api.fetchConfig1),
    api2: call(Api.fetchConfig2),
  });
  ...
}

Babel Macro

You can use the built-in babel macro that will take care of transforming all your effects to raw redux-saga effects.

Install the babel macros plugin:

yarn add --dev babel-plugin-macros

Modify your import names to use the macro:

import {call, race} from "typed-redux-saga/macro";

// And use the library normally
function* myEffect() {
  yield* call(() => "foo");
}

The previous code will be transpiled at compile time to raw redux-saga effects:

import {call, race} from "redux-saga/effects";

function* myEffect() {
  yield call(() => 'foo');
}

This gives you all the benefits of strong types during development without the overhead induced by all the calls to typed-redux-saga's proxies.

ESLint Rules

In order to avoid accidentally importing the original effects instead of the typed effects, you can use this ESLint plugin: https://github.com/jambit/eslint-plugin-typed-redux-saga

It includes an auto-fix option, so you can use it to easily convert your codebase from redux-saga to typed-redux-saga!

Credits

Thanks to all the contributors and especially thanks to @gilbsgilbs for his huge contribution.

See Also