Home > Blockchain >  Typescript: "new this" in extended method returns the base
Typescript: "new this" in extended method returns the base

Time:10-28

So I'm trying to build this models system and I want the load method to return the 'User' type instead of 'Model'.

In Javascript, I belive it works, but in Typescript, the type always refers to the model.

Is it possible to make it return the User type.

class Model {
    constructor() { }
    static load() {
        return new this();
    }
}

class User extends Model { }

let user = User.load(); 
// let user: Model <-- I wanted it to be of type User

Thank you

CodePudding user response:

There is no direct support for polymorphic this types for the static side of classes. See microsoft/TypeScript#5863 for the relevant feature request. Luckily you can emulate the behavior you want by using a this parameter instead and making the load() method generic in the polymorphic instance type. Like this:

class Model {
    constructor() { }
    static load<T extends Model>(this: new () => T) {
        return new this();
    }
}

Essentially the load() method can only be called on an object whose this context is a no-arg constructor. The instance type of this is the generic type parameter T, and so return new this() produces the return type of T. Therefore, XXX.load() should return the same type as the instance type of XXX.


Let's test it out:

class User extends Model {
    name = "abc"
}
let user = User.load(); // let user: User
console.log(user.name.toUpperCase()) // "ABC"

Looks good, now user is of type User as desired.


Also, note that you have to call it on a class whose constructor needs no arguments. If you create a class that doesn't work this way:

class Confuser extends Model {
    constructor(public name: string) {
        super();
    }
}

Then it will inherit the static load() method but the compiler won't let you call it:

let confuser = Confuser.load(); // error!
// Type 'new (name: string) => Confuser' is not assignable to type 'new () => Confuser'

which is a good thing, because new this() will fail to supply the required constructor argument, and thus could lead to runtime problems:

console.log(confuser.name.toUpperCase()) // compiler is happy, but:
//            
  • Related