LoginSignup
6
3

More than 3 years have passed since last update.

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

Last updated at Posted at 2020-02-21

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());
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3