Home > Net >  Generic type for all class methods names with return type
Generic type for all class methods names with return type

Time:03-11

I am attempting to create a generic which will give me information about all class methods return types ( fields, but they are not that important).

Let's say I have created such a class:

class TestClass {
    testField: string;

    constructor() {
        this.testField = 'test';
    }

    testMethod1(param: number): number {
        return param;
    }

    testMethod2(param: string): string {
        return param;
    }

}

As the result, I would like to have such type:

interface TestClassMethodsNamesReturnType {
  testMethod1: number;
  testMethod2: string;
}

What I have tried so far

  • mapping types types inference:

    Example 1

    export type ClassMethodsNamesReturnType<T extends Function> = {
        [k in keyof T['prototype']]: ReturnType<T['prototype'][k]>;
    }
    
    const x: ClassMethodsNamesReturnType<TestClass> = {
        //...
    }
    

    Error:

    Type 'TestClass' is missing the following properties from type 'Function': apply, call, bind, prototype, and 5 more

  • mapping types and treat class as object:

    Example 2

    // I think here I can have problems to decide is the key of my class is a field or method 
    // what can be a problematic to decide what I should use (typeof T[K] or ReturnType<typeof T[K]>)
    export type ClassMethodsNamesReturnType<T extends Record<string, T[K]>, K extends keyof T> = {
        [K]: T[K];
    }
    

    Error:

    A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type

    'K' only refers to a type, but is being used as a value here

Do you have any hints or ideas how can I achieve this?

CodePudding user response:

You can do this via mapped types although you would need to do a conditional filter using the as clause in the mapped type. Then, check if the value extends Function and then return never if it does not so it will not be mapped over. Here's the full code:

class TestClass {
    testField: string;

    constructor() {
        this.testField = 'test';
    }

    testMethod1(param: number): number {
        return param;
    }

    testMethod2(param: string): string {
        return param;
    }

}

type FnReturns<T> = { [K in keyof T as T[K] extends Function ? K : never]: ReturnType<T[K] extends (...args: any[]) => any ? T[K] : never> };

// Correctly passes:
const foo: FnReturns<InstanceType<typeof TestClass>> = {
    testMethod1: 23,
    testMethod2: "hey",
}

// correctly fails:
const fail: FnReturns<InstanceType<typeof TestClass>> = {}

TypeScript Playground Link

Also note how I make use of the InstanceType of typeof TestClass to get the instance methods and properties of TestClass.

  • Related