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.
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..