I am having a problem with these classes. I want to use the method doSomething() exclusive to class B without type casting it every time, but when I specify property a to be of type B, it tells me that it is not assigned in the constructor, which is somewhat false as the parent constructor does the assignment.
class A {
}
class B extends A {
doSomething() { }
}
class One {
constructor(protected a: A){ }
}
class Two extends One {
protected a: B // Property 'a' has no initializer and is not definitely assigned in the constructor.
constructor(){
super(new B());
// If I enter "this.a = new B();" here then the error disappears, but the code is redundant.
}
doStuff() {
this.a.doSomething()
}
}
What am I doing wrong?
The problem is that the proposal for adding class field declarations to JavaScript has different semantics from what you might expect and what TypeScript designers expected when they added them to TypeScript. It turns out that in JavaScript, class field declarations will be initialized via
Object.defineProperty()and not by assignment, and all such declared fields without initializers will be initalized withundefined. So eventually you can expect code like yours to generate JavaScript that setsatoundefinedin the subclass even though your intention is just to narrow the type from the base class. Blecch.So, in TypeScript 3.7, a
--useDefineForClassFieldsflag was added, along with thedeclareproperty modifier. If you use--useDefineForClassFields, the compiler will output code that is compliant with the expectedObject.defineProperty()semantics for class fields:And if you run your code as-is with that flag, you'll see the issue at runtime:
The solution is to use the
declareproperty modifier to narrow the subclass property without emitting any correspondingObject.defineProperty()code:Playground link to code