5

I'm trying to create a method that receives as a parameter some method name (as string) of a given class.

So for example if i have this code:

class A {
    private member1: string;
    public member2: number;

    method1() {
        //something
    }

    method2() {
        //something
    }

    propMethod = () => {
        //prop method something
    }
}

type OnlyClassMethods<T> = T; // need the correct type here

const a = new A();
function callMethod(methodName: OnlyClassMethods<A>) {
    // methodName is of type "method1" | "method2"
    a[methodName](); // to call this
}

then methodName will be resolved correctly and the intellisense would know that this is indeed a method of A. In reality i'm going to use the method name for mocks, so i'm not really calling it (so this should catch any method regardless of parameters pass or return value) but i still want to ensure that it's only methods that exist on the class.

Edit: I managed to fix my specific issue with both class methods and property methods, but i'll leave the question as is for anyone else. For a solution that could also distinguish between the two types, see Karol Majewski's solution below

2 Answers 2

5

The type you need is this:

type OnlyClassMethods<T> = {
    [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never
}[keyof T]

callMethod('method1') // ok
callMethod('foo') // error
callMethod('member1')// error

This is a pretty common mapped type that excludes non-function property names.

Playground

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

4 Comments

i'm getting that methodName is of type: "member2" | "method1" | "method2" | "propMethod", whereas i'm looking for just the methods
I missed the part that you want to divide "methods" from "members with a function value". But this is a wrong objective. They are both methods for any third party, there is absolutely no difference from that point of view. Hence, I don't know what you are trying to achieve and why. This is not possible and should not be.
your answer is correct, i forgot to put a type that's not 'any' on the member1/member2. in my scenario i'm also trying to distinguish the "typescript" properties from methods due to issues i'm having with react test kit.
@Jellof Glad I could help!
2

It's possible to distinguish between proper methods and class properties, but it requires explicit type annotations for this. Consider this example:

type FunctionPropertyOf<T> = {
    [P in keyof T]: T[P] extends Function
      ? P
      : never
}[keyof T]

type MethodOf<T> = {
    [P in keyof T]: T[P] extends (this: infer U) => any
      ? U extends T
        ? P
        : never
      : never
}[keyof T]

Usage:

class Foo {
    bar(this: Foo) {}
    baz = () => {}
}

type T1 = FunctionPropertyOf<Foo> // "bar" | "baz"
type T2 = MethodOf<Foo>           // "bar"

1 Comment

+1, this indeed has the value of distinguishing between them since you can't put "this: MyClass" in property methods.

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.