I have a saga defined as the following:
type GenericFunction = (...args: any[]) => any;
interface IFetchSaga<T extends GenericFunction> {
saga: T,
args: Parameters<T>
}
function* triggerChange<T extends GenericFunction>(fetchSaga: IFetchSaga<T>, shouldFetch: boolean) {
// ...do stuff
if(shouldFetch) {
yield call(fetchSaga.saga, ...fetchSaga.args);
}
// ...do stuff
}
When I try to use this saga inside another one, using the call
effect I am not getting the correct typing for the IFetchSaga
args:
function* fetchData(id: number, date: string) {
// ...do fetch
}
function* load() {
// ...do stuff
yield call(triggerChange, {
saga: fetchData,
args: ['10', '2021-03-22'] // I don't get an error saying that '10' should be a number
},
true
);
}
For instance, when I try to execute it directly (as if it was any other function), the typing works correctly:
triggerChange({
saga: fetchData,
args: ['10', '2021-03-22'] // Here I get the error saying that '10' should be a number
});
Ps.: I know I can define my fetchSaga
param outside of the call effect, but then I would have to always call the IFetchSaga
with a typeof
(and I would like to avoid it):
function* fetchData(id: number, date: string) {
// ...do fetch
}
function* load() {
// ...do stuff
// This would work, but I want to avoid having to do it 😬
const fetchSaga: IFetchSaga<typeof fetchData> = {
saga: fetchData,
args: ['10', '2021-03-22'] // Get the typing error for '10'
};
yield call(triggerChange, fetchSaga, true);
}
My suggestion to fix this is to create the IFetchSaga<T>
object through a helper function.
function fetchable<T extends GenericFunction>( saga: T, ...args: Parameters<T>): IFetchSaga<T> {
return {saga, args};
}
You could pass the args as an array if you want to.
This allows typescript to easily infer the proper args for the function and make sure that they match.
function* load() {
// error: Argument of type 'string' is not assignable to parameter of type 'number'
yield call(triggerChange, fetchable(fetchData, "10", "2021-03-22"), true);
// okay :)
yield call(triggerChange, fetchable(fetchData, 10, "2021-03-22"), true);
}
The reason that the original version fails is similar to this answer where I go more in depth. triggerChange
is a generic function that can be called with any T
. When we are creating a call
effect for triggerChange
it does not need to have any particular T
type since the T
is determined at the time when the function is called. The type for the arguments falls back to just GenericFunction
where args
are any[]
, so there is no issue with assigning your array to that.
Our solution works because we are inferring a specific T
by directly calling fetchable
. It's the fetchable
function that gives us errors -- not the call
. If we were to declare that the fetchable
T
is just GenericFunction
then we would have the same issue as before.
// no errors, even though we want them
yield call(triggerChange, fetchable<GenericFunction>(fetchData, "10", "2021-03-22"), true);