TypeScriptでクラス定数をプロパティとして定義できない件を記事にした。
理由としてはおそらくECMAScript由来の仕様だろうと思い調べてみたところ、 ES6(ES2015)では定数に限らずデータのプロパティをクラスのブロック直下に定義できない ことがわかった。
class Test{
prop = 1; //error! Unexpected token (2:6)
}
ES6でのデータのプロパティの持たせ方
仕様ではクラス直下の本体部分で定義できるのはメソッドのみでデータを保持する変数や定数は定義できない。
ClassBody[Yield] :
ClassElementList[?Yield]
ClassElementList[Yield] :
ClassElement[?Yield]
ClassElementList[?Yield] ClassElement[?Yield]
ClassElement[Yield] :
MethodDefinition[?Yield]
static MethodDefinition[?Yield]
;
データのクラスプロパティ自体が持てない訳ではなく、メソッドの内部でthis.prop = 1;
のように定義するか、クラスのインスタンスに後からプロパティをくっつける事で出来る。
class Test{
constructor(){
this.prop = 1; //ok
}
}
var a = new Test();
console.log(a.prop); //1
a.prop2 = "str";
console.log(a.prop2); //str
クラス定数も出来ない事は無い。
class Test{
constructor(){
}
get MAX(){
return 10000;
}
}
var a = new Test();
console.log(a.MAX); //10000
a.MAX = 0; //error! 代入不可
TypeScriptでは
TypeScriptではクラスのデータプロパティをクラスの直下で定義できる。さらに、public
/private
/protected
等のアクセス修飾子でアクセスの制御もできる。
OOPをやるならカプセル化のためにアクセス制御が必要なのでECMAScript側の仕様が固まる前に実装しているのだろう1。まあ、普通。
class Test{
prop = 1; //ok
static prop2 = 3; //ok
private prop3 = 1; //ok
protected prop4 = 1; //ok
}
class Test2 extends Test{
get prop5(){
return this.prop4;
}
}
var a = new Test();
console.log(a.prop); //1
console.log(Test.prop2); //3
console.log(a.prop3); //error!
console.log(a.prop4); //error!
var b = new Test2();
console.log(b.prop4); //error!
console.log(b.prop5); //1
Babelでは
experimentalフラグをONにすればいける。ES.nextで仕様検討中のため。
class Test{
prop = 1; //ok, experimental on
}
なお、フラグをONにしなくても、値を代入しない定義だけならエラーにならなかった。これがES6の仕様に即しているのかはわからない。
class Test{
prop;
}
以下、愚痴。何が問題なのか。
-
this.prop
で宣言するのはインデントが一つ深いのでパッと見理解しにくい - 同一スコープなんだから、同じインデントにしようよ
- 他のクラスベースの言語に合わせようよ
- 使いにくいオリジナリティとか要らないから
- クラスレベルのスコープの定数が定義しにくい
- ただでさえスコープ制御が貧弱なんだからもう少しなんとかならなかったの?
-
Number.MAX_SAFE_INTEGER
みたいなのを簡単に定義させてほしい - アンチパターンとしてクラスプロパティの宣言時に値を入れておきたくないというのはわかる
- staticなプロパティや定数までできなくしたのはやり過ぎじゃ?
- そのくせ宣言後のクラスインスタンスにプロパティ生やせちゃうのは…どうにかならなかったん?
- IDEの補完にむいてなさそう
- 最近の各IDEの補完は賢いからこれは杞憂に終わるかも
- ES.nextー!早く来てくれー!
-
ただ何故かクラス定数は簡単に定義できない。 ↩