Help us understand the problem. What is going on with this article?

【TypeScriptの罠】親クラスのコンストラクタ内で子クラスのメソッドを実行ー>何故か親クラスのメンバ変数の値が

TypeScriptでちょっとハマったのでメモ。

qiita.ts
abstract class Parent {
    protected hoge: string = "Parent";
    constructor() {
        this.foo();
    }
    public abstract foo();
    public bar() {
        console.log(this.hoge);
    }
}

class Child extends Parent {
    protected hoge: string = "Child";
    constructor() {
        super();
    }
    public foo() {
        console.log(this.hoge);
    }

}

const child: Parent = new Child(); // Parent
child.foo();// Child
child.bar();// Child

上記のコードだとC#やJavaの感覚では、
Childが3回出力されるものだと勘違いしてあせった。
想定した出力(実際、JavaやC#で似たようなことをするとこのように出力される)

Child
Child
Child

実際の出力は

Parent
Child
Child

になる

これを把握せずにこんなコードを書くと・・・

qiita.ts
abstract class Parent {
    protected hoge: string = "Parent";
    protected speech: string = "";
    constructor() {
        this.init();
    }
    public abstract init();
    public bar() {
        console.log(this.hoge);
    }
    getSpeech() {
        return this.speech;
    }
}

class Child extends Parent {
    protected hoge: string = "Child";
    constructor() {
        super();
    }
    public init() {
        this.speech = `私の名前は${this.hoge}です。`
    }
}

const child: Parent = new Child();
console.log(child.getSpeech());

出力結果

私の名前はParentです。

となってちょっと悩みました。

期待した出力

私の名前はChildです。

なぜそうなるか

TypeScriptではC#やJavaのようにクラスにメンバ変数を直接書くことができる。
が、JavaScriptではそのような書き方はできず、コンストラクタ内で定義することになる。

エラーになるコード

//JavaScript
class Hoge{
 foo="a"
}

正しいJavaScript

//JavaScript
class Hoge{
 constructor(){
  this.foo="a"
 }
}

なのでTypeScriptの以下のコードはビルドされると

//TypeScript
class Hoge{
 foo="a"
}

このようなJavaScriptを吐き出します。

//JavaScript
class Hoge {
    constructor() {
        this.foo = "a";
    }
}

つまり先程のよくわからない挙動をしていたTypeScriptは以下のようなJavaScriptになるためにC#やJavaに慣れ親しんだ人にとっては意図しない挙動になったということでした。
子クラスのメンバ変数に値が定義される前に親クラスのコンストラクタが実行されていることがわかります。
メンバ変数の定義がsuper()のあとに入れられてしまうという仕様のせいでした。

//TSCでコンパイルされたJavaScript
class Parent {
    constructor() {
        this.hoge = "Parent";
        this.speech = "";
        this.init();
    }
    bar() {
        console.log(this.hoge);
    }
    getSpeech() {
        return this.speech;
    }
}
class Child extends Parent {
    constructor() {
        super();//<-ここに注目
        this.hoge = "Child";///<-ここに注目
    }
    init() {
        this.speech = `私の名前は${this.hoge}です。`;
    }
}
const child = new Child();
console.log(child.getSpeech());
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした