web-dev-qa-db-ja.com

TypeScript:派生クラスのコンストラクターで「this」にアクセスする前に、「super」を呼び出す必要があります

私は以前にこの質問を数回見たことがありますが、私の質問は、これのアーキテクチャアプローチに関するものだと思います。
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では):
「派生クラスのコンストラクターで「this」にアクセスする前に、「super」を呼び出す必要があります」

可能な解決策

A)supercallで_this.lastName = lastName_を切り替える

_class Employee extends Person
{
    ...

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

    ...
}
_

=>ここでの問題は、_this.lastName_がクラス 'Employee'のscream()メソッド内でundefinedになることです。

B)
_setTimeout(callback, 0)を使用します。このようにして、this.scream()メソッドが後で呼び出されます。

_class Person 
{
    ...

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

    ...
}
_

=>しかし、それは私にとって非常に醜いハックのように感じます。

C)
Personクラス内でthis.scream() fromを呼び出さずに、コンシューマから呼び出します。

_const employee: Employee = new Employee();
employee.scream();
_

=>しかし、明らかにこれは常にあなたが望むものではありません。

質問

  • 私はここで馬鹿げたことをしていますか?
  • これを行う必要がないようにコードを調整するより良い方法はありますか?
  • このエラーを回避する方法はありますか?
15
dotdotcommadot

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

どちらも良い点だと思いますが、実際にはファクトリーパターンを使うべきです。

4
dotdotcommadot

ここで私は馬鹿げたことをしていますか?

はい、そうです。 iberbeuがコメントで述べたように、コンストラクターは、オブジェクトのconstructingに関係のないことは何もすべきではありません。あらゆる種類の予期しない行動につながる可能性があるのは、悪い習慣の事例です。

これを行う必要がないようにコードを配置するより良い方法はありますか?

[〜#〜] c [〜#〜]オプションで提供したソリューションを使用して、ここに進みます。

このエラーを回避する方法はありますか?

実際に何をしたいかによります。 通常のやり方は、[〜#〜] c [〜#〜]オプション。発生している問題が実際に複雑なオブジェクトをインスタンス化することに関連している場合は、ビルダー/ファクトリーパターンを調べてください。しかし、実際にコンストラクターにdoを実行させたい場合は、単に間違っているだけです。コンストラクタはアクションを実行するためのメンターではなく、オブジェクトを構築するためのものであり、他には何もありません。

3
Nypan

私のコメントと@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);
    }

}

私はまだこれを行うことをお勧めしませんが、あなたが本当にそれを必要とするとあなたがそれを適切に行うことを気にしない場合、これは1つの解決策になる可能性があります

1
iberbeu