コンストラクタからフィールドの型推論を
次に紹介するクラス構文に関する新機能は、バージョン4.0で導入されたコンストラクタからのフィールドの型推論です。
フィールドの型指定を省略
例えば、リスト1のBMIDataクラスの(1)〜(3)のそれぞれのフィールドには、データ型が記述されています。これは、以下のように初期値を記述することで、その初期値からの型推論が働くため、データ型指定を省略できます。
#name = "";
この型推論が、コンストラクタでの値代入から働くようにバージョン4.0で改良されました。この仕組みを利用すると、BMIDataクラスは、リスト5のように記述できます。
class BMIData { #name; // (1) #height; // (2) #weight; // (3) constructor(name: string, height: number, weight: number) { this.#name = name; // (4) this.#height = height; // (5) this.#weight = weight; // (6) } : }
リスト5のBMIDataクラスでは、(1)〜(3)のフィールドには型指定も初期値も記述していません。にもかかわらず、図2のように、#nameはstringと型推論されています。
この原因は、(4)のコードにあります。(4)でフィールドの#nameにstring型のnameを代入しているため、この型推論によりデータ型が決まるようになっています。(2)の#heightも(5)のコードでnumber型に、(3)の#weightも(6)のコードで同じくnumber型になります。
ただし、このコンストラクタからの型推論が働くのは、コンパイルオプションとして、noImplicitAnyが有効な場合のみなのには注意しておいてください。
型推論の注意すべき点
また、この型推論は、コンストラクタ内での値の代入で決まりますので、必ずしも引数の受け渡しである必要はありません。例えば、以下のコードのように、何か値を代入することで、フィールドの型は決定される点には留意しておいてください。
constructor(…) { this.#name = "名無し"; : }
さらに、このフィールドの型推論に関して注意しておかなければならないのは、型推論が働くのは、あくまでコンストラクタの処理のみということです。例えば、initialize()というメソッドを用意し、リスト6のようにコンストラクタからこのinitialize()メソッドを呼び出したとしても、#weightへの型推論は働かず、#weightはany型となってしまうので注意してください。
class BMIData { #name; #height; #weight; constructor(name: string, height: number, weight: number) { this.#name = name; this.#height = height; this.initialize(weight); } initialize(weight: number) { this.#weight = weight; } : }
子クラスでのsuper()の位置
コンストラクタ続きで、次に紹介するのは、子クラスでのsuper()の位置に関する変更です。バージョン4.6で導入された地味な変更ですが、それなりの影響力もあるので紹介します。
コンストラクタ内でのsuper()の位置に注意
例えば、リスト1のBMIDataを継承したBMIDataWithAgeクラスを考えたとします。このクラスでは、コンストラクタ内で親クラスであるBMIDataのコンストラクタをsuper()で呼び出す必要があるため、リスト7のようなコードになります。
class BMIDataWithAge extends BMIData { #age: number; constructor(name: string, height: number, weight: number, age: number) { // (1) console.log("初期化されました"); // (2) super(name, height, weight); // (3) this.#age = age; // (4) } }
リスト7では、(1)でコンストラクタを定義し、(3)で親クラスのコンストラクタを呼び出し、引数を渡しています。(4)がこのクラスのコンストラクタの独自の処理であり、フィールドに引数を代入しています。
ここで問題となるのが、(2)です。これまでsuper()を記述する位置は、コンストラクタ内で一番先頭という約束事でした。したがって、(2)のように、super()より前にコードを記述すると、エラーとなっていました。これが、バージョン4.6で変更され、現在、(2)のコードはエラーとなりません。
コンストラクタ内でのsuper()の位置の条件
ただし、この仕組みには制約があり、super()より前に記述できるコードにはthisアクセスを含みません。そのため、例えば、(4)のコードを以下のようにsuper()より前に記述すると、依然エラーとなるので、注意してください。
constructor(name: string, height: number, weight: number, age: number) { this.#age = age; super(name, height, weight); }