It is not currently possible in TypeScript to use this types in the call signature for a class constructor() 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 constructor method is unique in that it is a static method whose this context is that of a class instance, so presumably implementing this typing 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 static method with this types instead of the constructor, so you call B.make({}) instead of new B({}).
Unfortunately this types are also not supported for static methods. Support is requested at microsoft/TypeScript#5863. But you can simulate such behavior with this parameters and a generic type parameter, as mentioned in this comment on that GitHub issue:
abstract class A {
static make<T extends A>(
this: new (initializer: Partial<A>) => T,
initializer: Partial<T>
) {
return new this(initializer);
}
constructor(initializer: Partial<A>) {
for (const [key, value] of Object.entries(initializer)) {
Object.defineProperty(this, key, { value, enumerable: true })
}
}
static someStaticMethod() { }
someInstancemethod() { }
}
The make() method can only be called on a concrete constructor which accepts a Partial<A> and produces an instance of type T constrained to A. So you can't call A.make() directly for the same reason you can't call new A(), since the constructor is abstract:
A.make({}) // error!
// <-- Cannot assign an abstract constructor type to a non-abstract constructor type
But you can subclass as desired, and the static make() method will be inherited:
class B extends A {
a?: string
b?: number
c?: boolean
}
When you call B.make(), then, the compiler infers that T is B and so the parameter to make() is Partial<B>, which, among other things, will reject excess properties:
const b = B.make({ a: "hello", b: 1, c: true }); // okay
// const b: B
console.log(b); // {a: "hello", b: 1, c: true}
B.make({ a: "hello", b: 1, c: true, d: "this must error" }); // error!
// -------------------------------> ~~~~~~~~~~~~~~~~~~~~
// Object literal may only specify known properties
Playground link to code