'typescript return type of instance method

how can I infer the return type of an instance method of an extending class? see psuedo code below

Note, this might not be possible, after looking into:

// Factory.ts
export default class Factory {
  static make(...params: any[]): FactorySubclass["make"] {
    // @ts-ignore
    return new this(...params).make();
  }

  make() {
    throw new Error("Not implemented");
  }
}

// CarFactory.ts
class CarFactory extends Factory {
  make() {
    return "hello";
  }
}

const instance = CarFactory.make(); // should infer "hello"


Solution 1:[1]

No need for the instanceof.

In an execution context, Factory['make'] refers to the make property of the Factory class, which in this case is the make() static method. In a type context, however, Factory['make'] refers to the make property of the Factory type, which in this case is the make() instance method.

So, this will work as per your requirements:

class Factory {
  static make(...params: any[]): ReturnType<Factory['make']> {
    return new this(...params).make();
  }

  make() {
    throw new Error("Not implemented");
  }
}

const x = Factory.make(); // correctly infers the type of x as "void"

The TypeScript 2.1 release notes refer to this notation as indexed access types or lookup types.

type m = Factory['make']; // defines the type m as "() => void"

Update

For the updated question, to get the return type of the make() method of an extending class to be inferred correctly, you can do so by using a generic parameter to refer to the class type's constructor (see here), and making sure that the base class' make() instance method's return type is compatible with that of the implementing class, e.g. by making it return any:

class Factory {
  static make<T extends Factory>(
    this: { new (...params: any[]): T },
    ...params: any[]
  ): ReturnType<T['make']> {
    return new this(...params).make();
  }

  make(): any {
    throw new Error('Not implemented');
  }
}

class CarFactory extends Factory {
  make() {
    return 'hello';
  }
}

const x = Factory.make(); // infers the type of x as "any"
const y = CarFactory.make(); // infers the type of y as "string"

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 goldylucks