Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

TypeScript: self-referencing return type for static methods in inheriting classes

With Polymorphic this in TypeScript 1.7, as I discovered here, we can define a method in a class with a return type of this, and automatically, any classes that extend that class and inherit the methods, will have their return types set to their respective this type. Like so:

class Model {   save():this {    // return type: Model     // save the current instance and return it   } }  class SomeModel extends Model {   // inherits the save() method - return type: SomeModel } 

However, what I'm after is to have an inherited static method with a return type referencing the class itself. It's best described in code:

class Model {   static getAll():Model[] {     // return all recorded instances of Model as an array   }    save():this {     // save the current instance and return it   } }  class SomeModel extends Model {   // inherits the save() method - return type: SomeModel   // also inherits getAll() - return type: Model (how can we make that SomeModel?) } 

Perhaps I'll have to think of a different way to implement this, since Polymorphic this in TypeScript 1.7 does not support static methods by design.

EDIT: I guess we'll see how this Github issue wraps up: https://github.com/Microsoft/TypeScript/issues/5863

like image 411
Merott Avatar asked Dec 04 '15 21:12

Merott


People also ask

Can you refer static method through class reference?

Static methods are the methods in Java that can be called without creating an object of class. They are referenced by the class name itself or reference to the Object of that class.

Can static methods be inherited JavaScript?

Static Methods Are Inherited When Using ES6 Extends Syntax In JavaScript And Node.

Does TypeScript support static classes?

The class or constructor cannot be static in TypeScript.


2 Answers

This is doable in TypeScript 2.0+. By using an inline { new(): T } type to capture this, you'll get what you wanted:

type Constructor<T> = { new (): T }  class BaseModel {   static getAll<T>(this: Constructor<T>): T[] {     return [] // dummy impl   }      /**    * Example of static method with an argument:    */   static getById<T>(this: Constructor<T>, id: number): T | undefined {     return // dummy impl   }    save(): this {     return this // dummy impl   } }  class SubModel extends BaseModel {}  const sub = new SubModel() const savedSub: SubModel = sub.save()  // Behold: SubModel.getAll() returns SubModels, not BaseModel const savedSubs: SubModel[] = SubModel.getAll() 

Note that getAll still expects no arguments with this typing.

For more information, see https://www.typescriptlang.org/docs/handbook/2/generics.html#using-class-types-in-generics and https://stackoverflow.com/a/45262288/1268016

like image 92
mrm Avatar answered Sep 19 '22 02:09

mrm


Based on the simplest answer to the GitHub issue, you can use InstanceType<> like this:

class Foo {     static create<T extends typeof Foo>(this: T): InstanceType<T> {         return new this() as InstanceType<T>     }      static getAll<T extends typeof Foo>(this: T): Array<InstanceType<T>> {         return []     } }  class Bar extends Foo { }  const a = Bar.getAll() // typeof a is Bar[] const b = Bar.create() // typeof b is Bar. 

Where I threw in the create function just for illustration, from the linked GitHub example.

like image 35
Brian M. Hunt Avatar answered Sep 17 '22 02:09

Brian M. Hunt