I've seen this question passing a few times before, but I think my question is more concerning an architectural approach of this.
In TypeScript it is not possible to use the this
keyword before calling super
(on a class that extends from another class).
But what if you need to do something as in the example below?
(Just for clarification: I'm creating a component lifecycle for a UI library, so it feels like I really need to do something like this, and I can't seem to think of any other way of tackling this).
What I would like to do is this:
class Person
{
public firstName: string;
constructor()
{
this.scream();
}
protected scream(): void
{
console.log(this.firstName);
}
}
class Employee extends Person
{
public lastName: string;
constructor(firstName: string, lastName: string)
{
this.lastName = lastName;
super(firstName);
}
protected scream(): void
{
console.log(this.firstName + ' ' + this.lastName);
}
}
The constructor of the parent class, 'Person', calls a protected method.
The child class, 'Employee', wants to use its own parameter (this.lastName
) when overriding this protected method.
But the code above is throwing the error (in Webstorm at least):
"'super' must be called before before accessing 'this' in the constructor of a derived class"
A) Switch this.lastName = lastName
with the super
call
class Employee extends Person
{
...
constructor(firstName: string, lastName: string)
{
super(firstName);
this.lastName = lastName;
}
...
}
=> The problem here is that this.lastName
will be undefined
inside the scream()
method on class 'Employee'.
B)
Use setTimeout(callback, 0)
.
This way the this.scream()
method will be called later.
class Person
{
...
constructor()
{
setTimeout(() => this.scream(), 0);
}
...
}
=> But it just feels like a very ugly hack to me.
C)
Don't call this.scream()
from inside the Person class, but call it from the consumer.
const employee: Employee = new Employee();
employee.scream();
=> But obviously this is not always what you want.
The super keyword can be used in expressions to reference base class properties and the base class constructor. Super calls consist of the keyword super followed by an argument list enclosed in parentheses. Super calls are only permitted in constructors of derived classes.
The super keyword is used to access properties on an object literal or class's [[Prototype]], or invoke a superclass's constructor. The super. prop and super[expr] expressions are valid in any method definition in both classes and object literals. The super(... args) expression is valid in class constructors.
Another solution I eventually came up with, in addition to the ones provided by @iberbeu and @Nypan, is to add and intermediary initProps()
method right before the call to scream()
:
class Person
{
public firstName: string;
constructor(firstName: string, props?: any)
{
this.firstName = firstName;
this.initProps(props);
this.scream();
}
protected initProps(props: any): void
{
}
protected scream(): void
{
console.log(this.firstName);
}
}
class Employee extends Person
{
public lastName: string;
constructor(firstName: string, lastName: string)
{
super(firstName, {lastName});
}
protected initProps(props: any): void
{
this.lastName = props.lastName;
}
protected scream(): void
{
console.log(this.firstName + ' ' + this.lastName);
}
}
Although I think both made a strong point and I should actually be using a factory pattern instead..
Am I doing a dumb thing here?
Yes you are. As iberbeu said in his comment a constructor should never do anything that does not have to do with constructing the object. It is a case of bad practice that can lead to all sorts of unexpected behaviour.
Are there better ways to arrange my code so I don't need to do this?
Using the solution you provided in your C option is the way to go here.
Is there some way to work around this error?
It depends on what you actually want to do. The normal way of doing things is illustrated by yourself in your C option. If the problem you are having is related to actually instantiating complex objects you might want to look in to builder/factory patterns. But if you actually want the constructors to do something you are simply doing it wrong; constructors are not ment to perform actions, they are there to construct objects and nothing else.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With