typed-redux-saga
typed-redux-saga copied to clipboard
yield* call sometimes infers type as Generator
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 })
)
);
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
works in the sense it stops typescript complaining but breaks runtime behaviour
I may have caused this in https://github.com/agiledigital/typed-redux-saga/issues/13
Could you formulate this as a dtslist $ExpectType
failure in index.test.ts?
Hi @danielnixon you can see an example in #92 . Thanks!
PS more than happy to tidy up the PR if you find it useful
hi is there any update on this?
PRs welcome
I was struggling with this as well, and in the end it was my fault, because I was call
ing a saga which yield
ed 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.
Thanks @ivan-aksamentov that's a good insight. I wonder if there's something we can do with eslint to catch that 🤔
That might also explain #27
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
@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?
https://github.com/agiledigital/typed-redux-saga/blob/bec739d2c418559e1a4247ea1e4576d753f17f3b/types/index.d.ts#L567
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>>>
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>