Búsqueda de sitios web

herencia - TypeScript: 'super' debe ser llamado antes de acceder a 'this' en el constructor de una clase derivada


He visto esta pregunta pasar algunas veces antes, pero creo que mi pregunta se refiere más a un enfoque arquitectónico de esto.
En TypeScript no es posible usar la palabra clave this antes de llamar a super (en una clase que extiende de otra clase).
Pero, ¿y si necesitas hacer algo como en el ejemplo a continuación?
(Sólo para aclarar: estoy creando un ciclo de vida de componentes para una biblioteca de UI, así que siento que realmente necesito hacer algo como esto, y no parece que pueda pensar en otra manera de abordarlo).

Código

Lo que me gustaría hacer es esto:

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);
    }
}

Problema

El constructor de la clase padre, 'Person', llama a un método protegido.
La clase hija, 'Employee', quiere usar su propio parámetro (this.lastName) al sobrescribir este método protegido.
Pero el código anterior está lanzando el error (al menos en Webstorm):
""'super' debe ser llamado antes de acceder a 'this' en el constructor de una clase derivada"

Solución posible

A) Cambia this.lastName=lastName por la llamada a super

class Employee extends Person
{
    ...

    constructor(firstName: string, lastName: string)
    {
        super(firstName);
        this.lastName = lastName;
    }

    ...
}

=> El problema aquí es que this.lastName será undefined dentro del método scream() en la clase 'Employee'.

B)
Usa setTimeout(callback, 0). De esta manera, el método this.scream() será llamado más tarde.

class Person 
{
    ...

    constructor()
    {
        setTimeout(() => this.scream(), 0);
    }

    ...
}

=> Pero simplemente me parece un truco muy feo.

C)
No llames a this.scream() desde dentro de la clase Person, sino llámala desde el consumidor.

const employee: Employee = new Employee();
employee.scream();

=> Pero obviamente esto no siempre es lo que quieres.

Pregunta

  • ¿Estoy haciendo una tontería aquí?
  • ¿Hay mejores formas de organizar mi código para que no tenga que hacer esto?
  • ¿Hay alguna manera de solucionar este error?

Respuesta #1

Otra solución que eventualmente se me ocurrió, además de las proporcionadas por @iberbeu y @Nypan, es añadir un método intermediario initProps() justo antes de la llamada a 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);
    }
}

Aunque creo que ambos hicieron un punto fuerte y en realidad debería estar usando un patrón de fábrica en su lugar..

Respuesta #2

¿Estoy haciendo algo tonto aquí?

Sí, lo eres. Como dijo iberbeu en su comentario, un constructor nunca debería hacer nada que no tenga que ver con construir el objeto. Es un caso de mala práctica que puede llevar a todo tipo de comportamientos inesperados.

¿Hay mejores maneras de organizar mi código para que no tenga que hacer esto?

Usar la solución que proporcionaste en tu opción C es el camino a seguir aquí.

¿Hay alguna manera de solucionar este error?

Depende de lo que realmente quieras hacer. La forma normal de hacer las cosas está ilustrada por ti mismo en tu opción C. Si el problema que estás teniendo está relacionado con la instanciación de objetos complejos, podrías querer investigar los patrones de constructor/fábrica. Pero si realmente quieres que los constructores hagan algo, simplemente lo estás haciendo mal; los constructores no están destinados a realizar acciones, están ahí para construir objetos y nada más.

Artículos relacionados