Typescript(クラス)

はじめに

javascriptでも馴染み深いクラスについてTypescriptでの記法を確認していきます。クラスについてよくわからないと言う方は以前の記事
ES6(クラス) - 駆け足エンジニアの記録
を参考にしてもらえると嬉しいです。

Typescriptでのクラスの定義

class Person {
  name: string; //this.nameの型指定
  age: number; //this.ageの型指定
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  hello() {
    console.log(`hello ${this.name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト
bob.hello(); //hello Bob

コンストラクター関数の引数にあるような引数の型指定は、一般的な関数同様です。クラスならではの型指定は、今回の例でいうとclassとconstoructorに挟まれたname:stringです。クラスではletやconstで変数名を宣言できないため、その代わりにthis.をプロパティ名の前に付けます。このthis+プロパティ名はクラス内のメソッドで多用されます。そのプロパティ名(name)引数のnameコンストラクター関数内で紐付けています。(ageも同様です。)

アクセス修飾子

ここで一つ問題点があります。コードで確認しましょう。(クラスは上記と同じPersonクラスです。)

const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト
bob.name = 'Green'//変数名nameを書き換え
bob.hello(); //hello Green

このようにクラス内の変数名をクラス外で書き換え可能の状態です。これを改善するためには、変数名の前にアクセス修飾子privateをつけます。

class Person {
  private name: string; //アクセス修飾子private
  age: number; //デフォルトではpublic(このように省略可)
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  hello() {
    console.log(`hello ${this.name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト
bob.name = "Green"; //エラー!(プロパティ 'name' はプライベートで、クラス 'Person' 内でのみアクセスできます。)
bob.hello(); //hello Green(Typescriptは静的な言語なのでコンソール上ではhello Greenが生成されます。)

またprivateを#で表現することも可能です。

class Person {
  #name: string; //アクセス修飾子private
  age: number; //デフォルトではpublic(このように省略可)
  constructor(name: string, age: number) {
    this.#name = name;
    this.age = age;
  }
  hello() {
    console.log(`hello ${this.#name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト
bob.hello(); //hello Green(Typescriptは静的な言語なのでコンソール上ではhello Greenが生成されます。)
クラスの省略記法

また大体の場合プロジェクトにはたくさんのクラスが存在し、そのクラスの内部にはいくつかのプロパティが存在します。それらには初期値を設定しなければならないので、毎回プロパティ名引数を紐付けるのは面倒です。そこでこの面倒事を改善してくれる省略記法があります。

class Person {
  constructor(private name: string, public age: number) {}
  hello() {
    console.log(`hello ${this.name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト

これが最初に提示したPersonクラスの省略形です。かなり簡潔にかけます。注意すべきは、アクセス修飾子は変数名の前に必ずつけなければならないということです。(たとえデフォルトのpublicでも)

readonly修飾子

ここまでクラス外で書き換え不可にするためのprivate修飾子を導入しましたが、クラス内では書き換えられます。

class Person {
  private gender: string = "male";
  constructor(private name: string, public age: number) {}
  hello() {
    this.name = "Green";//nameプロパティを書き換えられてしまう。
    console.log(`hello ${this.name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト
bob.hello();//hello Green

ここでの改善策はreadonly修飾子を使って、フィールドを読み取り専用と指定することです。つまり、あるフィールド(Personクラス)に初期値を当てた(インスタンス時)を割り当てたあとでそのフィールドを変更できないことを宣言できます。(constのように)

class Person {
  constructor(private readonly name: string, public age: number) {}
  hello() {
    this.name = "Green"; //エラー(読み取り専用プロパティであるため、'name' に代入することはできません。)
    console.log(`hello ${this.name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト