简体   繁体   中英

How to pass a newable `this` to a static method from a static method?

How do I pass a newable this to a static method from a static method?

TypeScript Playground

abstract class Model {
  public static convert<T extends Model>(model: new () => T, data: any | any[]) { 
    return new model()
  }

  public static all<T extends Model>(): Promise<[]> {
    let items: any[] = []
    return Model.convert(this, items)
                      // ^--- Error is here
  }
}

The resulting JavaScript works, and I get an instance of A , however typescript complains with this error:

Argument of type 'typeof Model' is not assignable to parameter of type 'new () => Model'.

 class Model { static convert(model, data) { return new model() } static all() { let items = []; return Model.convert(this, items); } } class A extends Model { } console.log(A.all(), A.all().constructor.name); 

This would normally work if Model were not abstract . If Model was not abstract this (which is typed as typeof Model ) would have the new () => Model constructor, but since it is abstract, it does not have a callable constructor.

The simple solution is to add an annotation for this in the static method:

abstract class Model {
  public static convert<T extends Model>(model: new () => T, data: any | any[]): T[] { return null!; }

  public static all<T extends Model>(this: new () => T): Promise<T[]> {
    let items: any[] = []
    return Promise.resolve(Model.convert(this, items));
  }
}

class A extends Model { }


console.log(Model.all()) // err since Model does not have a ctor
console.log(A.all(), A.all().constructor.name)

Play

This will make all un-callable for Model which is probably what you want anyway.

Edit

Since in your actual use case you want to call static methods from other static methods we need a slightly more complex annotation for this . The previous version just keeps the constructor signature and none of the static members. You need the static members represented by typeof Model in the annotation. The simplest way to do this is to add typeof Model in an intersection with the constructor signature we previously defined ( (new () => T) ) basically adding a constructor to the abstract class.


type NonAbstracModel<T extends Model> = (new () => T) & typeof Model
abstract class Model {
  public static convert<T extends Model>(model: NonAbstracModel<T>, data: any | any[]): T { return new model() }

  public static all<T extends Model>(this: NonAbstracModel<T>): T {
    let items: any[] = []
    return Model.convert(this, items)
  }

  public static find<T extends Model>(this: NonAbstracModel<T>, primaryKey: string | object) { }

  public static firstOrFail<T extends Model>(this:NonAbstracModel<T>) {
    this.find('abc')
  }

}

class A extends Model {}

console.log(A.all(), A.all().constructor.name)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM