I have the following class with a constructor that defines properties on it. I need the constructor to accept an object which only contains the (extending) class members
abstract class A {
constructor(initializer: Partial<this>) { // <-- using this type in a constructor is not allowed!
for (const [key,value] of Object.entries(initializer)) {
Object.defineProperty(this, key, { value, enumerable: true })
}
}
static someStaticMethod() {}
someInstancemethod() {}
}
this class is only used to be extended by other classes
class B extends A {
a?: string
b?: number
c?: boolean
}
new B({ a: "hello", b: 1, c: true, d: "this must error" }) // <-- I want only properties of B here in the constructor argument
I know I can add a type argument to A and then assign the extending class to it
abstract class A<T> {
constructor(initializer: Partial<T>) {
...
}
}
class B extends A<B> {
...
}
But this solution of repeating B doesn't look elegant, especially considering that this is an external API for a library I'm working on. Is it possible to achieve what I want, leaving the syntax as in the first example: class B extends A? If not, what is a possible workaround?
Playground Link
It is not currently possible in TypeScript to use
thistypes in the call signature for a classconstructor()method. There is an open feature request at microsoft/TypeScript#38038 asking for such support, but for now it's not part of the language.(A comment asked why it isn't already supported, but I don't have an authoritative answer for that. I can guess: The
constructormethod is unique in that it is a static method whosethiscontext is that of a class instance, so presumably implementingthistyping for its call signature would have required some conscious effort to do. And it's not a feature the community is clamoring for, as evidenced by the relatively few upvotes on the issue linked above. Those are probably contributing factors for why it is not already supported. But, as I said, these are guesses.)Until and unless that is implemented you'd need to use workarounds.
One such workaround would be to use a
staticmethod withthistypes instead of the constructor, so you callB.make({})instead ofnew B({}).Unfortunately
thistypes are also not supported for static methods. Support is requested at microsoft/TypeScript#5863. But you can simulate such behavior withthisparameters and a generic type parameter, as mentioned in this comment on that GitHub issue:The
make()method can only be called on a concrete constructor which accepts aPartial<A>and produces an instance of typeTconstrained toA. So you can't callA.make()directly for the same reason you can't callnew A(), since the constructor is abstract:But you can subclass as desired, and the static
make()method will be inherited:When you call
B.make(), then, the compiler infers thatTisBand so the parameter tomake()isPartial<B>, which, among other things, will reject excess properties:Playground link to code