redux-saga-test-plan icon indicating copy to clipboard operation
redux-saga-test-plan copied to clipboard

Unexpected typescript type error with expectSaga

Open Yupeng-li opened this issue 3 years ago • 0 comments

Not all the generator functions pass the type check when using expectSaga. Sometimes unexpected type error is raised if the yields in the function are mixed by saga effects and normal generator functions.

// Example
import { expectSaga } from "redux-saga-test-plan";
import { select } from "redux-saga/effects";

function* dummyGenerator(name: string) {
  yield select();
  console.log(name);
}

function* dummySagaFn() {
  const name: string = yield select(); // Type the return value. Important to reproduce the issue
  yield dummyGenerator(name);
}

it("", async () => {
  expectSaga(dummySagaFn).withState({ name: "test-name" }).run(); // type error 
});

Sample code (line 15): https://codesandbox.io/s/cranky-resonance-47uuz?file=/src/demo.test.ts Typescript version: 4.5

// Source code

interface Iterator<T, TReturn = any, TNext = undefined> {
    // NOTE: 'next' is defined using a tuple to ensure we report the correct assignability errors in all places.
    next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
    return?(value?: TReturn): IteratorResult<T, TReturn>;
    throw?(e?: any): IteratorResult<T, TReturn>;
}

interface IterableIterator<T> extends Iterator<T> {
    [Symbol.iterator](): IterableIterator<T>;
}

export type SagaType = (...params: any[]) => SagaIterator | IterableIterator<any>;

export const expectSaga: (<S extends SagaType>(
  generator: S,
  ...sagaArgs: Parameters<S>
) => ExpectApi) & { DEFAULT_TIMEOUT: number };

The issue is in SagaType. IterableIterator<any> extends Iterator<T>. The TNext is default to undefined. If we type one of the return values of yield statements (line 10 in the example), the error occurs.

Potential solution: Change SagaType to export type SagaType = (...params: any[]) => SagaIterator | Generator<any>;

Yupeng-li avatar Dec 02 '21 10:12 Yupeng-li