It's very doable! You will want your deploy function to have a generic type parameter that represents the type of the function. You can make use of the built-in utility types Parameters<T> and ReturnType<T> to get the arguments and return type of the function.
I'm not exactly sure how your C extends myType comes into play here. It seems like it's the return type of the function? You could use C as a second generic type parameter but I would recommend leaving it off.
export const deploy = async <T extends (...args: any[]) => any>(
func: T,
...args: Parameters<T>
): Promise<ReturnType<T>> => {
return func(...args);
}
We say that deploy is based on a type T which must be a function. The first argument of deploy is the function T and the remaining arguments are the arguments of that function. deploy returns a Promise which resolves to the return type of the function T.
This allows the usages that you want and gives errors if there is a mismatch between the function and the arguments.
// If alpha function
deploy(alpha, "hello", "world");
// If beta function
deploy(beta, 1, 2);
// Expect error
deploy(alpha, 1, 2); // Argument of type 'number' is not assignable to parameter of type 'string'.(2345)
Typescript Playground Link
If you wanted to use your C type, that might look something like this. We say that T is a function which must return a value that is assignable to C.
type myType = {
a: string;
};
const alpha = (a: string, b:string) => ({a, b});
const beta = (a: number, b:number) => ({a, b});
export const deploy = async <C extends myType, T extends (...args: any[]) => C>(
func: T,
...args: Parameters<T>
): Promise<C> => {
return func(...args);
}
The alpha function is still fine because it returns {a: string; b: string;} which extends {a: string}. But you can no longer use the beta function because its return type is not assignable to myType.
So the C parameter allows you to enforce a restriction on what type of function you can deploy with.
Typescript Playground Link