0

I'm trying to declare a generic type based on another generic type without success.

The objective is the write my own test framework and to type some parameters depending on another one (the method).

type Arguments<T> = T extends (...args: infer U) => any ? U : never;

// my custom test method
const methodCall = <T extends (...args: any) => any>(args: {
  method: T;
  response: ReturnType<T>;
  myArguments: Arguments<T>;
}): boolean => {
  const { method, myArguments, response } = args;
  return method.apply(null, myArguments) === response;
};

const test1 = (toto: string) => {
  return toto === "success";
};

// usage of my custom function
methodCall({
  method: test1,
  myArguments: ["fail"],
  response: false
});


// this is what I want to type
interface MyHelpers {
  methodCall: any // HOW TO TYPE THIS? 
  methodCall2: (args: { flag: boolean }) => boolean;
}

// I would expose only the helpers object
const helpers = (): MyHelpers = {
  methodCall: <T extends (...args: any) => any>(args: {
    method: T;
    response: ReturnType<T>;
    myArguments: Arguments<T>;
  }): boolean => {
    const { method, myArguments, response } = args;
    return method.apply(null, myArguments) === response;
  },
  methodCall2: (args: { flag: boolean }): boolean => {
    return args.flag;
  }
};

I'm expecting another object calling helpers to be able with helpers().methodCall(...) to be typed as it is declared in helpers. Not with any.

The playground can be found here.

thanks!

1 Answer 1

1

You're quite close, if I understand you correctly. You can choose between function syntax (where you define the signature of methodCall as if you're defining a function implementation) and property syntax (where you define methodCall as a property that happens to be a lambda, which is close to how you have it).

If you use the function syntax, you define the generic in angle brackets (<>), the parameter list between parentheses, and the return type after a colon; it's as if you're defining a function without a body. If you use the property syntax, you're defining a lambda, with the generic in angle brackets, the parameter list between parentheses, and the return type after a fat arrow (=>); you're defining a property by that name and putting your type—a function type—after the colon. Either way will work for you.

(I've also switched from your custom Arguments utility type to the undocumented Parameters built-in.)

interface MyHelpers {
  methodCall<T extends (...args: any) => any>(args: {
    method: T;
    response: ReturnType<T>;
    myArguments: Parameters<T>;
  }): boolean;
  methodCall2: (args: { flag: boolean }) => boolean;
}

interface MyHelpersWithObjectSyntax {
  methodCall: <T extends (...args: any) => any>(args: {
    method: T;
    response: ReturnType<T>;
    myArguments: Parameters<T>;
  }) => boolean;
  methodCall2: (args: { flag: boolean }) => boolean;
}

typescript playground

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.