I want to create a generic function that only allows calls to functions specified in an interface.
interface MyApi {
add(x: number, y:number) : number;
say(s:string): string;
A: number;
}
type OnlyFuncs<T> = Pick<T, { [K in keyof T]: T[K] extends (...args:any) => any ? K : never }[keyof T]>;
class ApiClient<T, Tfunc = OnlyFuncs<T>> {
public call<K extends keyof Tfunc, T2 = Tfunc[K]>(method:K, p:Parameters<T2>) {
}
}
I'd like to able to use my ApiClient class to call the two methods 'add' and 'say' from the function 'call'. I've managed to "filter out" member A from the MyApi interface, but I can't get the correct type T2 to get the parameters right for the 'p' argument.
Why is T2 not seen by the compiler as a function?
CodePudding user response:
You just need to constrain the object type represented by your generic T
:
type AnyFn = (...args: any) => any;
type AnyObj = Record<PropertyKey, any>;
type OnlyFuncs<T extends AnyObj> = {
[K in keyof T as T[K] extends AnyFn ? K : never]: T[K];
};
interface MyApi {
add(x: number, y: number) : number;
say(s: string): string;
A: number;
}
class ApiClient<T extends AnyObj> {
public call<K extends keyof OnlyFuncs<T>>(
method: K,
p: Parameters<T[K]>,
) {}
}
const client = new ApiClient<MyApi>();
client.call('say', ['hello']);
client.call('add', [2, 3]);
client.call('A', []); /*
~~~
Argument of type '"A"' is not assignable to parameter of type '"say" | "add"'.(2345) */
type AllowedMethod = Parameters<typeof client.call>[0];
//^? type AllowedMethod = "say" | "add"