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
Bas a generic argument is a pretty standard pattern called the curiously recurring template pattern. I'll agree that it's not the most elegant solution, but it is pretty standard.privateto work in your subclasses?Partial<B>has no access to thea,b, orcproperties. When you write "I know I can add a type argument toAand then assign the extending class to it", have you tried that? It's impossible to pass ina,b, orcthere. Could you fix this in your question so there's only one stumbling block and not multiple?thisin static methods, but not in the constructor itself. If this addresses your question I can write up an answer explaining it with relevant documentation. If not, what am I missing?