TypeScriptで 'Uncaught TypeError: this.arrow is not a function'

はじめに

表題のエラーに遭遇したので、解決方法を探りました。 この記事は、表題の問題の詳細と解決方法を共有するためのものです。

発生環境

TypeScript 3.7.2

問題の詳細

発生した問題

コンストラクタ内で特定のアロー関数がundefinedとなります。

pen.js:32 Uncaught TypeError: this.arrow is not a function
    at ExtendClass.init (pen.js:32)
    at ExtendClass.BaseClass (pen.js:14)
    at new ExtendClass (pen.js:24)
    at pen.js:36

問題が発生する最小限のコード

class BaseClass {
  constructor()
  {
    this.init();
  }
  
  init():void{
    console.log("init Base Class");
  }
}

class ExtendClass extends BaseClass{
  constructor(){
    super();
  }
  
  init():void{
    super.init();
    this.arrow();
  }
  
  arrow = () =>{
    console.log("Arrow func");
  }
}

const instance = new ExtendClass();
console.log( instance );

CodePenはこちらです。

問題の発生手順

問題が発生する手順は

  1. 基底クラスを定義する
  2. 基底クラスのコンストラクタ内で関数を呼び出す
  3. 基底クラスを継承した拡張クラスを定義する
  4. 拡張クラスで手順2の関数をオーバーライドする
  5. 手順4の関数内部でアロー関数を呼び出す

となります。

問題の原因

TypeScript Playgroundで上記のコードをJavaScriptに変換すると、以下のコードとなります。

"use strict";
class BaseClass {
    constructor() {
        this.init();
    }
    init() {
        console.log("init Base Class");
    }
}
class ExtendClass extends BaseClass {
    constructor() {
        super();
        this.arrow = () => {
            console.log("Arrow func");
        };
    }
    init() {
        super.init();
        this.arrow();
    }
}
const instance = new ExtendClass();
console.log(instance);

原因はここです。

    constructor() {
        super();
        this.arrow = () => {
            console.log("Arrow func");
        };
    }

アロー関数がコンストラクタの内部で宣言されています。したがってExtendClass.init関数が実行された時点でarrow関数はまだ定義されていません。

解決方法

アロー関数ではなく通常の関数にする

class ExtendClass extends BaseClass{
...中略...
  arrow():void{
    console.log("Arrow func");
  }
}

アロー関数を使う必要がないなら、通常の関数に置き換えるのが最も簡単な解決方法です。

オーバーライドした関数からは呼び出さず、コンストラクタ内から呼び出す

class ExtendClass extends BaseClass{
  constructor(){
    super();
    this.arrow();
  }
  
  arrow = () =>{
    console.log("Arrow func");
  }
}

設計上アロー関数でなくてはいけない場合、コンストラクタの内部で呼び出すことでこの問題を回避できます。 アロー関数は定義後に実行されます。

以上、ありがとうございました。