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:
//