Issue
This is a simplified version of my real issue
I have an external API similar to this:
interface CommonRequestParams {
user: {};
}
function f1(params: CommonRequestParams) {/* do something */}
interface SecondRequestParams extends CommonRequestParams {
payload: { id: string };
}
function f2(params: SecondRequestParams) {/* do something */}
interface ThirdRequestParams extends CommonRequestParams {
payload?: { id: number };
}
function f3(params: ThirdRequestParams) {/* do something */}
const api = {
f1,
f2,
f3,
};
I created a wrap function to provide common data for method calls.
My problem is with the types, when dealing with optional properties (payload can be optional)
see "problematic part" marked with a comment
// api methods could be used like this:
// f1({ user: {} });
// f2({ user: {}, payload: { id: "1" } });
// f3({ user: {} });
// f3({ user: {}, payload: { id: 1 } });
type RequestApi = typeof api;
type RequestApiMethod = RequestApi[keyof RequestApi];
type RequestApiMethodPayload<M extends RequestApiMethod, P = Parameters<M>[0], > =
// I think this is the problematic part
P extends CommonRequestParams & { payload?: any }
? P["payload"]
: never;
function wrap<M extends RequestApiMethod, >(method: M) {
return (payload: RequestApiMethodPayload<M>) => {
return method({
user: {},
payload,
});
};
}
Expected results commented below
const _f1 = wrap(f1);
const _f2 = wrap(f2);
const _f3 = wrap(f3);
_f1(); // this should be OK
_f2({ id: "1" }); // OK
_f3(); // this should be OK
_f3({ id: 1 }); // OK
_f1("unknown"); // this should NOT be ok
( I tried with more conditions / using infer { payload?: infer Payload }... ) but no success
Solution
You only need to check if the payload key exists in P:
type RequestApiMethodPayload<M extends RequestApiMethod, P = Parameters<M>[0]> = "payload" extends keyof P ? P["payload"] : undefined;
This is similar to the in operator in JavaScript ("payload" in P).
Then in the return type you would check if this type includes undefined, and if it does, make the parameter optional:
function wrap<M extends RequestApiMethod>(method: M):
undefined extends RequestApiMethodPayload<M>
? (params?: RequestApiMethodPayload<M>) => ReturnType<M>
: (params: RequestApiMethodPayload<M>) => ReturnType<M>;
However, you'll get an error on the line that adds the payload... you could try an assertion but I just ignored it. Anyways, it works as expected now.
Answered By - caTS
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.