I have a class with mixed member types, fields and methods (ex. MyClass below).
I am trying to have a command created that calls a function and is able to execute it while keeping this context.
The code below works if I use a class that has no fields in it, only callable members ( ex. if i comment out public field: number = 1;) This works fine, good type sense, errors if class method does not exist, errors if bad params. but it kind of does not like it when i pass a class that has non-callable members.
Maybe you guys have a solution for that?
class MyClass{
constructor() {}
public field: number = 1;
public printMessage(): void{
console.log("message")
}
public printNumber(i: number): number{
return i;
}
}
type TypeOfClassMethod<T, M extends keyof T> = T[M] extends Function ? T[M] : never;
interface ICommand{
execute(): any;
}
class Command<F extends keyof MyClass> implements ICommand{
api: MyClass;
constructor(
api: MyClass,
private func: TypeOfClassMethod<MyClass, F>,
private args: Parameters<TypeOfClassMethod<MyClass, F>> //
) {
this.api = api;
}
execute(): ReturnType<TypeOfClassMethod<MyClass, F>>{
// error[1] type ‘TypeOfClassMethod<MyClass, F>’ does not satisfy the constraint ‘(...args: any) => any’.
// Type ‘Function & MyClass[F]’ is not assignable to type ‘(...args: any) => any’.
return this.func.call(this.api, ...this.args)
}
}
let instance = new MyClass();
const command = new Command<"printNumber">(instance, instance.printNumber, [5] );
const wrongCOmmand = new Command<"field">(instance, instance.field, [] );
EDIT: My main problem with the implementation i had is that it gives errors if i have non-function members in a class, I want it to work for other classes that have non-executable members but give me an error if I want to create a command with a member that is not executable.
Basically I want to call this in a wrapper function callWithRetry which gets passed a command and it executes it until it succeeds or a certain break criteria are met.
Take a look here, as an example (TS playground)
Or this one which is based on your first answer's second option
Both lack return type when I try to call it inside a function.
function executeCommand < InstanceType > (command: Command < InstanceType > ) {
// i want to use this in a manner similar to this, in a retry mechanism, but in this case I lose the returnType
return command.execute()
}
const result = executeCommand(command); // result will have a return type of any
UPDATE: I got it to work how I wanted it, thank you so much for the help! here is the solution in TS playground