クラスはclass
で定義できます。またメソッドの定義の仕方やゲッターセッターなどだいたい JavaScript と同じです。
値の初期化
constructor
で定義します。
class A {
value: number;
constructor(value: number) {
this.value = value;
}
}
先に使うプロパティの型だけ定義しておいて、constructor
でそこに代入します。TypeScript ではこれを、以下のような短縮記法があり、先程の書き方とまったく同じ結果になります。
class A {
constructor(public value: number) {
}
}
public
とアクセス修飾子を付けています。これは他にprivate
、protected
があり、それらは以下のそれぞれ意味があります。
public
どこからでもアクセス可能protected
それ自身、または継承したものからだけアクセス可能private
それ自身からしかアクセスできない
例えば、private
を使う場合はこうなります。
class A {
constructor(private value: number) {
}
}
public
を書き換えただけですね。
constructor 以外で値の初期化
TypeScript のstrictPropertyInitialization
設定を有効にしているとconstructor
で初期化していないプロパティは警告を受けます。例えば以下のような感じのときです。
class A {
// Property 'value' has no initializer
// and is not definitely assigned in the constructor.
value: number;
componentDidMount() {
this.value = 123;
}
}
もしcomponentDidMount
が100%呼ばれるからvalue
にはnumber
が入っているんだと自信がある場合は、
class A {
value!: number;
componentDidMount() {
this.value = 123;
}
}
value!: number;
のように!
を付けることで警告を回避できます。
スタティック値
スタティック値とは、プロトタイプではないそれ自信のプロパティ(クラス自体に生やす)の値のことです。これを定義するにはpublic
のように頭にstatic
と書くだけです。
class A {
static foo = 'static foo';
}
A.foo; // 'static foo'
ジェネリクスなクラス
クラス名のあとにジェネリクス型の設定を書くことができます。
class A {
// ...
}
constructor
まで自分で定義しているなら動的にT
を解決できるかもしれませんし、もし設定してからじゃないと使えない場合は、
const a = new A();
// T === Foo
のように型を指定して使うことができます。
プライベートフィールド
TypeScript@3.8 (2020-02-20) よりプライベートフィールドが使える様になりました。これはプロパティ宣言名に#
プレフィックスを付けることでprivate
プロパティのようにそのクラス内でのみ扱うことができるプロパティになります。
class A {
#foo: string;
constructor(foo: string) {
this.#foo = foo;
}
}
上記のようなコードがあるとして、外部から#foo
にアクセスしようとするとエラーになります。
new A("a").#foo;
// Property '#foo' is not accessible outside class 'A' because it has a private identifier.
// プライベート識別子を持つので外部から`#foo`にアクセスできません。
ちなみにこの書き方だと短縮記法は使えません。
class A {
// error
constructor(private #foo: string) {
}
}
JavaScript ではどうなっているか
プライベートフィールド毎にWeakMap
を作り、クラスのインスタンスをキーに値を保存しているようです。
// 簡略化してます
let _foo;
class A {
constructor(foo) {
_foo.set(this, void 0);
_foo.set(this, foo);
}
}
_foo = new WeakMap();
プライベートアクセス修飾子との違い
スーパークラスで定義されたプライベートアクセス修飾子なプロパティはサブクラスで再定義できません。
class SuperA { private value = 123; }
// Class 'A' incorrectly extends base class 'SuperA'. // Types have separate declarations of a private property 'value'. class A extends SuperA { private value = 456; }
プライベートフィールドの場合、クラス内からのみアクセスできるという制約を守りながらスーパー/サブクラス毎に値を設定できます。
```ts
class SuperA {
#value = 123;
getFromSuperA() {
return this.#value;
}
}
class A extends SuperA {
#value = 456;
// 上書きしたいならコメントアウト
// getFromSuperA() {}
getFromA() {
return this.#value;
}
}
console.assert(new SuperA().getFromSuperA() === 123);
console.assert(new A().getFromSuperA() === 123);
console.assert(new A().getFromSuperA() === 456);
またプライベートアクセス修飾子なプロパティはstring
によるアクセスを許してますが、プライベートフィールドは許していないという違いもあります。
class A {
private value = 123;
}
// アクセスできてしまう
console.assert(new A()["value"] === 123);
class B {
#value = 123;
}
// アクセスできない
// Property '#value' does not exist on type 'A'.
console.assert(new B()["#value"] === 123);