Reputation: 3224
I have a resolver function which might have an optional second parameter called otherParams. I would like that Typescript checks if the function returned from createResourceThunk is also called with the parameter otherParams if it is defined in the resolver function. How can I do that? Minimal reproducable example here
export function createResourceThunk<T, Params, A, OtherParams extends A | never>(
actions: ResourceActions<T>,
resolver: (params: Params, otherParams: OtherParams) => Promise<T>
) {
return function(params: Params, callbacks: Callbacks<T>, otherParams: OtherParams) {
return async (dispatch: Dispatch) => {
dispatch(actions.startAction());
try {
const data = await (otherParams ? resolver(params, otherParams) : resolver(params));
...
} catch (error) {
...
}
};
};
}
Currently it is expecting always a third Parameter in the call of the returned function
Upvotes: 0
Views: 59
Reputation: 328758
Given the code in your linked example, I'd be inclined to use the following typing:
export function createResourceThunk<T, P, O extends [] | [otherParams: any]>(
actions: ResourceActions<T>,
resolver: (params: P, ...args: O) => Promise<T>
) {
return function (params: P, callbacks: Callbacks<T>, ...otherParamsArr: O) {
const [otherParams] = otherParamsArr; // if you care
return async (dispatch: Dispatch) => {
dispatch(actions.startAction());
try {
const data = await resolver(params, ...otherParamsArr);
dispatch(actions.successAction({ data }));
callbacks && callbacks.onSuccess && callbacks.onSuccess({ data });
} catch (error) {
dispatch(actions.errorAction({ error }));
callbacks && callbacks.onError && callbacks.onError({ error });
}
};
}
}
The important piece here is that the "optional" second parameter is represented as a rest argument, whose type is a union of rest tuple types. The type parameter O
is the list of parameters after the params
argument. It is constrained to either have zero elements ([]
) or one element ([otherParams: any]
). So anywhere before where you'd want to say otherParams
you would use a rest parameter like ...otherParamsArr
.
That works inside the implementation with no errors or type assertions necessary. You can verify that this also works from the caller's side:
// with OptionalParam
const func1 = createResourceThunk('any', (a: { a: number }, b: string) => Promise.resolve('any'))
func1({ a: 1 }, { successAction: () => { } }, 'somestring') // okay
func1({ a: 1 }, { successAction: () => { } }); // error
// without OptionalParam
const func2 = createResourceThunk('any', (a: { a: number }) => Promise.resolve('any'))
func2({ a: 1 }, { successAction: () => { } }) // okay
func2({ a: 1 }, { successAction: () => { } }, 'somestring') // error
Upvotes: 1