Typescript -- instantiate child from a static method in base class, with args and referencing other static method

361 Views Asked by At

I am trying to instantiate a child class from a static method in a base class. I want to type my base class correctly rather than using any as the return type on all my static methods. I tried the solution here but it doesn't work for a static method that references other static methods, or accepts arguments. How can I correctly inherit from a base class in typescript, and still reference other methods and accept arguments?

class BaseClass {
  id: string;

  [key: string]: unknown;

  static getName() {
    return this.name.toUpperCase()
  }

  static async find<T extends BaseClass>(this: new (...args: any[]) => T, id: string)
  : Promise<T> {
    const tableName = this.getName();

    const result: GetResult = db.find(tableName, id);

    return new this(result);
  }
}

class Child extends BaseClass {
  name: string;

  static findOne(id: string): Promise<Child> {
    return this.find(id);
  }
}

Child.find('abcd');

This causes two different errors

  1. Property 'getName' does not exist on type 'new (...args: any[]) => T'. (in the find method)
  2. Type 'BaseModel' is missing the following properties from type 'Child': name. (in the return type for findOne method)
1

There are 1 best solutions below

10
Artyom Kozhemiakin On BEST ANSWER

In find method of the base class you should specify that it expects child class to implement static getName method, like this:

static async find<T>(this: { new (arg: GetResult): T } & typeof BaseClass, id: string): Promise<T>

Particularly { new (arg: GetResult): T } brings you constructor and typeof BaseClass brings you static members.

I mocked some missing parts and it typechecks.

type GetResult = string;

const db = {
    find: (a: string, b: string) => "bar",
}

class BaseClass {
  id: string = "bzzzz";

  [key: string]: unknown;

  static getName() {
    return 'NAME'
  }

  static async find<T>(this: { new (arg: GetResult): T } & typeof BaseClass, id: string)
  : Promise<T> {
    const tableName = this.getName();

    const result: GetResult = db.find(tableName, id);

    return new this(result);
  }
}

class Child extends BaseClass {
  name: string = "Child";

  static findOne(id: string): Promise<Child> {
    return this.find(id);
  }
}

Child.find('abcd');