TypeScript:' super'必须先致电,然后再访问这个'在派生类的构造函数中

时间:2016-05-30 10:23:05

标签: inheritance architecture typescript webstorm typescript1.8

我之前已经看过几次这个问题,但我认为我的问题更多的是关于这种架构方法。
在TypeScript中,在调用this之前不能使用super关键字(在从另一个类扩展的类上)。
但是如果你需要做一些事情,如下例所示呢? (仅仅是为了澄清:我正在为UI库创建一个组件生命周期,所以感觉我真的需要做这样的事情,我似乎无法想到任何其他方法来解决这个问题)

代码

我想做的是:

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

问题

父类的构造函数' Person',调用受保护的方法 在覆盖此受保护方法时,子类' Employee'想要使用自己的参数(this.lastName)。
但上面的代码抛出错误(至少在Webstorm中):
"'超'必须先致电,然后再访问这个'在派生类"

的构造函数中

可能的解决方案

A)使用this.lastName = lastName电话

切换super
class Employee extends Person
{
    ...

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

    ...
}

=>这里的问题是this.lastName在{'员工'上的undefined方法中将scream()

B)
使用setTimeout(callback, 0)。 这样,this.scream()方法将在稍后调用。

class Person 
{
    ...

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

    ...
}

=>但它对我来说只是一个非常难看的黑客。

C)
不要在Person类中调用this.scream(),而是从消费者处调用它。

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

=>但显然这并不总是你想要的。

问题

  • 我在这做蠢事吗?
  • 有没有更好的方法来安排我的代码,所以我不需要这样做?
  • 有什么方法可以解决此错误吗?

3 个答案:

答案 0 :(得分:3)

我在这里做蠢事吗?

是的,你是。正如iberbeu在他的评论中所说,构造函数永远不应该做任何与构造对象无关的事情。这是一种不良做法,可能导致各种意外行为。

有没有更好的方法来安排我的代码,所以我不需要这样做?

使用您在 C 选项中提供的解决方案即可。

有什么方法可以解决此错误吗?

这取决于你真正想做的事情。 正常做事方式由您自己在 C 选项中说明。如果您遇到的问题与实际实例化复杂对象有关,则可能需要查看构建器/工厂模式。但是,如果你真的希望构造函数某事你只是做错了;构造函数不执行动作,它们构造对象而不是其他任何东西。

答案 1 :(得分:3)

除了@iberbeu和@Nypan提供的解决方案之外,我最终提出的另一个解决方案是在调用initProps()之前添加和中间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);
    }
}

虽然我认为两者都很有用,但实际上我应该使用工厂模式。

答案 2 :(得分:1)

正如我在评论和@Nypan的回答中所写,你应该避免这样做。无论如何,有可能覆盖Child中的方法scream并调用一个新方法。看一下下面的代码

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)
    {
        super(firstName);
        this.lastName = lastName;
        this.screamOVerriden();
    }

    protected scream(): void {
        // do nothing
    }

    protected screamOverriden(): void {
        console.log(this.firstName + ' ' + this.lastName);
    }

}

我仍然不建议这样做,但如果你说你真的需要它并且你不在乎正确地做到这一点那么这可能是一个解决方案