TypeScript:在派生类的构造函数中访问“this”之前必须先调用“super”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/37523282/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-21 03:32:07  来源:igfitidea点击:

TypeScript: 'super' must be called before before accessing 'this' in the constructor of a derived class

inheritancearchitecturetypescriptwebstormtypescript1.8

提问by dotdotcommadot

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

我之前曾多次看到这个问题,但我认为我的问题更多地涉及这种架构方法。
在 TypeScript 中,不能this在调用之前使用关键字super(在从另一个类扩展的类上)。
但是,如果您需要执行以下示例中的操作怎么办?
(只是为了澄清:我正在为 UI 库创建一个组件生命周期,所以感觉我真的需要做这样的事情,我似乎想不出任何其他方法来解决这个问题)。

Code

代码

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

Problem

问题

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"

父类的构造函数“Person”调用受保护的方法。
子类“Employee”希望this.lastName在覆盖此受保护方法时使用其自己的参数 ( )。
但是上面的代码抛出了错误(至少在 Webstorm 中):
“在派生类的构造函数中访问‘this’之前必须调用‘super’”

Possible Solution

可能的解决方案

A) Switch this.lastName = lastNamewith the supercall

A)this.lastName = lastNamesuper电话切换

class Employee extends Person
{
    ...

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

    ...
}

=> The problem here is that this.lastNamewill be undefinedinside the scream()method on class 'Employee'.

=> 这里的问题是this.lastName将在类“Employee”undefinedscream()方法中。

B)
Use setTimeout(callback, 0). This way the this.scream()method will be called later.

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

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.

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

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

=> But obviously this is not always what you want.

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

Question

问题

  • Am I doing a dumb thing here?
  • Are there better ways to arrange my code so I don't need to do this?
  • Is there some way to work around this error?
  • 我在这里做傻事吗?
  • 有没有更好的方法来安排我的代码,所以我不需要这样做?
  • 有没有办法解决这个错误?

回答by dotdotcommadot

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():

除了@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);
    }
}

Although I think both made a strong point and I should actually be using a factory pattern instead..

虽然我认为两者都提出了一个强项,但我实际上应该使用工厂模式来代替..

回答by Nypan

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 constructingthe object. It is a case of bad practice that can lead to all sorts of unexpected behaviour.

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

Are there better ways to arrange my code so I don't need to do this?

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

Using the solution you provided in your Coption is the way to go here.

使用您在C选项中提供的解决方案是去这里的方法。

Is there some way to work around this error?

有没有办法解决这个错误?

It depends on what you actually want to do. The normalway of doing things is illustrated by yourself in your Coption. 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 dosomething you are simply doing it wrong; constructors are not ment to perform actions, they are there to construct objects and nothing else.

这取决于您实际想要做什么。在正常的做事方式是通过自己在你的说明Ç选项。如果您遇到的问题与实际实例化复杂对象有关,您可能需要查看构建器/工厂模式。但是,如果你真的想在构造函数一些你只是做错了; 构造函数不是用来执行动作的,它们只是用来构造对象而已。

回答by iberbeu

As I wrote in my comment and @Nypan in his answer, you should avoid doing this. Anyway, a possibility could be to override the method screamin the Child and call a new method. Take a look at the following code

正如我在评论中和@Nypan 在他的回答中所写的那样,您应该避免这样做。无论如何,一种可能性是覆盖screamChild 中的方法并调用一个新方法。看看下面的代码

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

}

I still don't recommend doing this but if you say you really need it and you don't care about doing it properly then this could be one solution

我仍然不建议这样做,但是如果您说您确实需要它并且不关心正确执行此操作,那么这可能是一种解决方案