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
CodePudding user response:
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.