Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

yield* call sometimes infers type as Generator #43

Open
rssfrncsrenishaw opened this issue Mar 18, 2020 · 13 comments
Open

yield* call sometimes infers type as Generator #43

rssfrncsrenishaw opened this issue Mar 18, 2020 · 13 comments
Labels
help wanted Extra attention is needed

Comments

@rssfrncsrenishaw
Copy link

rssfrncsrenishaw commented Mar 18, 2020

const { d1, d2  } = yield* all({
    d1: call(fetchD1, options),
    d2: call(fetchD2, options),
  });

I've had sitatuions where the above example fetchD1 and fetchD2 are both other sagas that I am calling with call but fetchD1 infers the correct type ({ from: string; to: string; featureName: string | undefined; name: string; data: MeasurementCharacteristicPresentation[]; }) and fetchD2 infers,

Generator<SelectEffect | AllEffect<SagaGenerator<{
    from: string;
    to: string;
    featureName: string | undefined;
    name: string;
    data: MeasurementCharacteristicPresentation[];
}, CallEffect<...>>>

The difference between the two sagas return,

fetchD1

return yield* call(
    (from, to, take, machineId) =>
      client.fetchD1Data.unique({
        query: {
          from,
          to,
          take,
          machineId
        }
      }).promise,
    from,
    to,
    constants.maxTake,
    machineId
  );

fetchD2

return yield* all(
    items.map(({  name }) =>
      call(fetchD2Data, { from, to,  name })
    )
  );
@rssfrncsrenishaw rssfrncsrenishaw changed the title yield* call sometimes infers type as CallEffect instead of resolve type yield* call sometimes infers type as Generator Mar 18, 2020
@rssfrncsrenishaw
Copy link
Author

Doing yield* yield* call() works... so it does seem to think i'm returning a generator though i'm not sure how I am as I am doing return yield* all

@rssfrncsrenishaw
Copy link
Author

works in the sense it stops typescript complaining but breaks runtime behaviour

@danielnixon
Copy link
Collaborator

I may have caused this in #13

@danielnixon danielnixon added the help wanted Extra attention is needed label May 6, 2020
@danielnixon
Copy link
Collaborator

Could you formulate this as a dtslist $ExpectType failure in index.test.ts?

@kristian-puccio
Copy link

Hi @danielnixon you can see an example in #92 .
Thanks!

PS more than happy to tidy up the PR if you find it useful

@rssfrncsrenishaw
Copy link
Author

hi is there any update on this?

@danielnixon
Copy link
Collaborator

PRs welcome

@ivan-aksamentov
Copy link

ivan-aksamentov commented Aug 14, 2020

I was struggling with this as well, and in the end it was my fault, because I was calling a saga which yielded a mixture of generators and non-generators in places where there is conditional program flow - because I forgot to check all the branches and to turn some of the yield to yield*.

TLDR: If you have this issue, first go and check all the callees recursively - you might have forgot to change yield to yield* somewhere and it waits for you to fix it.

Real example:

export function* parseSaga({ threadParse, input }: ParseParams) {
  yield put(parseAsync.started())
  try {
    const result = yield* call(threadParse, input)
    yield put(parseAsync.done({ result }))
    return result
  } catch (error) {
    yield put(parseAsync.failed({ error: sanitizeError(error) }))
  }
  return undefined
}

const parseResult = yield* call(parseSaga, { threadParse, input })
// type of parseResult is inferred as `Generator<bla bla bla>`

Notice how in parseSaga() I use yield* call(threadParse, input), like typed-redux-saga readme suggests, but I forgot to change all 3 yield put(...). The put() from typed-redux-saga returns a generator and you must use yield* with it. And if you now, then the type of the entire saga becomes a generator. So, if I change those to yield* put(...) (with an asterisk), then the type is inferred correctly.

It is a super silly mistake to make, but took me some time to figure. Hope it helps someone to resolve their type errors.

@danielnixon
Copy link
Collaborator

Thanks @ivan-aksamentov that's a good insight. I wonder if there's something we can do with eslint to catch that 🤔

@danielnixon
Copy link
Collaborator

That might also explain #27

@nzankich
Copy link

I ran into this when using eventChannel which isn't typed

If anyone needs a hack to get around this issue you can do something like this

const foo = *function() {
      // @ts-ignore
      const something = yield* call(bar) as ReturnType<typeof barTyped>;

     // something now has correct type
}

// Do not call.  Used for typing
const barTyped = function *() {
  return yield* yield* call(bar);
};

const bar = function *() {
   return yield call(something)
}

Avoiding yield is still the best choice if you have that option though

@rickyrombo
Copy link

@danielnixon I ran into this with race - Could this be because the RaceEffect<T[keyof T]> of the second type param to SagaGenerator being a RaceEffect<SagaGenerator<...>> and not a RaceEffect<CallEffect> etc? So the generator thinks it yields generators and not effects? I think it should be something like:
RaceEffect<T[keyof T] extends SagaGenerator<any, infer E> ? E : T[keyof T]> right?

Does that seem right?

RaceEffect<T[keyof T]>

  const raceEffect = race({
    timeout: delay(timeoutMs)
  })

has type:

const raceEffect: SagaGenerator<{
    timeout: true | undefined;
}, RaceEffect<SagaGenerator<true, CallEffect<true>>>>

but I think it should have the type:

const raceEffect: SagaGenerator<{
    timeout: true | undefined;
}, RaceEffect<CallEffect<true>>>

@pepijn-motosumo
Copy link

I think I may be running into this issue too. I opened a draft PR with a minimal example as a failing test: #669.

Test code repeated here for convenience:

  // $ExpectType boolean
  yield* Effects.call(function* () {
    yield* Effects.race({ timeout: Effects.delay(1) });
    return true;
  });

Test output for convenience:

[email protected] expected type to be:
  boolean
got:
  Generator<RaceEffect<SagaGenerator<true, CallEffect<true>>>, boolean, unknown>

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
help wanted Extra attention is needed
Projects
None yet
Development

No branches or pull requests

7 participants