Inspiraller
Inspiraller

Reputation: 3806

How to write type for redux-saga generator function containing yield* delegation in typescript

I am learning about generators and redux-saga. I have taken from an existing example which uses yield*. The first method - yieldPromiseResponse using 'yield' is ok. The second method - watchPromise which yields the result of yieldPromiseResponse using yield* errors with

Error: Type 'ForkEffect' must have a 'Symbol.iterator' method that returns an Iterator

Can someone advise me what type to apply to this example?

const at: TAt = {
  SUCCESS: 'SUCCESS',
};

interface Props {
  username: string;
}

interface GProps {
  payload: Props;
}

type Tprom = (payload: Props) => Promise<string>;

const prom: Tprom = username =>
  new Promise((resolve, reject) => {
    resolve(`resolve prom uesrname = ${username}`);
  });

function* yieldPromiseResponse({ payload }: GProps) {
  /* eslint-disable no-useless-catch */
  try {
    const user = yield call(prom, payload);
    console.log('user = ', user);
    yield put({ type: at.SUCCESS, user });
  } catch (error) {
    throw error;
  }
}

function* watchPromise() {
  yield* takeLatest(at.REQUEST, yieldPromiseResponse);
}


export default watchPromise;

Upvotes: 0

Views: 1383

Answers (1)

Inspiraller
Inspiraller

Reputation: 3806

There are 2 problems here. The first one I have managed to fix.

Problem 1

  1. When removing *
yield takeLatest.

This causes new error:

No overload matches this call. The last overload gave the following error. Argument of type 'string' is not assignable to parameter of type 'TakeableChannel'.

This is because typescript thinks I'm using takeLatest as channel, not as pattern. https://redux-saga.js.org/docs/api/

  • takeLatest(pattern, saga, ..args)
  • takeLatest(channel, saga, ..args)

Found the answer for the fix here: https://github.com/redux-saga/redux-saga/issues/1286

Updated code is now recognised in typescript without error

import { put, call, takeLatest } from 'redux-saga/effects';
import { Action } from 'redux';

const ActionTypeRequest = 'REQUEST';

interface Props {
  username: string;
}

interface ActionResult<T> extends Action<string> {
  type: string;
  payload?: T;
}

type Tprom = (payload: Props) => Promise<string>;

const prom: Tprom = payload =>
  new Promise((resolve, reject) => {
    resolve(`resolve prom uesrname = ${payload.username}`);
  });

function* yieldPromiseResponse(action: ActionResult<Props>) {
  /* eslint-disable no-useless-catch */
  const { payload } = action;
  try {
    const user = yield call(prom, payload as Props);

    yield put({ type: ActionTypeRequest, user });
  } catch (error) {
    throw error;
  }
}

function* watchPromise() {
  yield takeLatest(ActionTypeRequest, yieldPromiseResponse);
}

export default watchPromise;

Problem 2 Still does not work with

  yield* takeLatest(ActionTypeRequest, yieldPromiseResponse);

Maybe I need to understand the yield delegation - yield * When reading this - https://tech.lalilo.com/redux-saga-and-typescript-doing-it-right

When I change my code to this it does not complain. So maybe someone might be able to explain what is wrong with yield* on my takeLatest method.

function* secondaryGenerator() {
  yield delay(111);
  return true;
}

function* watchPromise() {
  yield takeLatest(ActionTypeRequest, yieldPromiseResponse);
  const secondary = yield* secondaryGenerator();
  console.log('second = ', secondary);
}

Upvotes: 3

Related Questions