Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

typescript return type of instance method

Tags:

typescript

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:

  • https://github.com/microsoft/TypeScript/issues/5863
  • https://github.com/Microsoft/TypeScript/issues/24364
  • Workaround for accessing class type arguments in static method in Typescript
// 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"
like image 431
goldylucks Avatar asked Mar 07 '26 07:03

goldylucks


1 Answers

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"
like image 140
Robby Cornelissen Avatar answered Mar 08 '26 21:03

Robby Cornelissen



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!