Home > Enterprise >  how to write a generic function that calls a method of an object
how to write a generic function that calls a method of an object

Time:10-16

There are two class:

class STRING_TYPE {
    name():string{
        return "one";
    }
}
class NUMBER_TYPE {
    name():number{
        return 1;
    }
}

I want to write a generic function:

  1. create an object of the given class
  2. call name() method, and return its value; In javascript, code like:
function foo(classType) {
    const obj = new classType();
    return obj.name();
}

How to write it in typescript?

// it doesn't work.
function foo<T>(typ: T): ReturnType<T.name>{
    const obj = new T();
    return obj.name();
}

CodePudding user response:

You need to put a constraint for classType argument:

class STRING_TYPE {
  value(): string {
    return "one";
  }
}
class NUMBER_TYPE {
  name(): number {
    return 1;
  }
}

type AnyClass<Return> = new (...args: any[]) => Return

const foo = <Klass extends AnyClass<{ name: () => number }>>(classType: Klass) =>
  new classType().name();
  
const result = foo(NUMBER_TYPE)

Playground

foo expects a class constructor with name method. TS is able to infer return type.

UPDATE

class STRING_TYPE {
  name(): string {
    return "one";
  }
}
class NUMBER_TYPE {
  name(): number {
    return 1;
  }
}

type AnyClass<R> = new (...args: any[]) => R

const foo = <
  Return extends { name: () => any },
  Klass extends AnyClass<Return>,
  >(classType: Klass): ReturnType<InstanceType<Klass>['name']> =>
  new classType().name()

foo(NUMBER_TYPE) // number
foo(STRING_TYPE) // string

CodePudding user response:

You can do something like this:

class STRING_TYPE {
  value(): string {
    return 'one';
  }
}

class NUMBER_TYPE {
  name(): number {
    return 1;
  }
}

function foo(classType: typeof STRING_TYPE): string; // ReturnType<STRING_TYPE['value']>
function foo(classType: typeof NUMBER_TYPE): number; // ReturnType<NUMBER_TYPE['name']>

function foo(classType: typeof STRING_TYPE | typeof NUMBER_TYPE) {
  const obj = new classType();
  return obj instanceof STRING_TYPE ? obj.value() : obj.name();
}

const bar = foo(STRING_TYPE);
const baz = foo(NUMBER_TYPE);
console.log(bar, baz);

Playground


Assuming you did a typo while asking question here, and both your classes have a name method, you can do this:

class STRING_TYPE {
  name(): string {
    return 'one';
  }
}

class NUMBER_TYPE {
  name(): number {
    return 1;
  }
}

type ClassType =
  | typeof STRING_TYPE
  | typeof NUMBER_TYPE;

function foo<T extends ClassType>(classType: T) {
  const obj = new classType();
  return obj.name() as ReturnType<InstanceType<T>['name']>;
}

const bar = foo(STRING_TYPE);
const baz = foo(NUMBER_TYPE);
console.log(bar, baz);

Playground


References:

  • Related