Typescript(クラス継承)

はじめに

この記事は前回記事
Typescript(クラス) - 駆け足エンジニアの記録の続きとなっています。
早速ですが、コードで確認しましょう。

Typescriptによるクラス継承

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

class Japanese extends Person {
       //継承先 extends 継承元
  constructor(name:string, age:number, private gender:'male'|'female') {
    super(name, age); //superメソッドで継承元のプロパティを継承先でも使える状態にします。
    this.gender = gender; //継承元のプロパティを受け継いだあとにJapanese独自のプロパティを設定。
  }
  hello() {//←継承元のメソッドを上書きすることができます。
    console.log("konnichiwa" + this.name); 
  }
}
const takashi = new Japanese("Takashi", 24, "male"); //生成されるインスタンスはオブジェクトです(しつこいようですが…)

コードについて、軽く解説を入れます。2つのクラスが存在し、JapaneseクラスがPerson クラスを継承しているという関係です。アクセス修飾子やreadonly修飾子についてご存じない方は前回記事参照でお願いします。また、Japaneseクラスのプロパティの一つにgenderがあります。この型は'male'|'female'、つまりリテラル型とユニオン型の複合となります。genderの値はrmaleかfemaleどちらかしか受け付けないということを意味します。

protected修飾子

さて上記のコードですが、現状tsファイルではエラーが出ています。

f:id:Tsuboi99553758:20200904093800p:plain
app.ts

このエラーの原因はnameプロパティを定義した際、private修飾子をつけていることにあります。private修飾子をつけたプロパティはそのクラス(Personクラス)からのみアクセスできるというものでした。それをJapaneseクラスで使おうとしているためエラーが発生しています。

このエラーを解消するためには、単にprivate修飾子をなくすか、アクセス修飾子の一つであるprotected修飾子を使うという改善策を以下に提示します。

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

privateをなくすだけだと、アクセス修飾子はpublic修飾子扱いとなり、readonlyがついているため書き換えは不可ですが、それでもクラス外からアクセス可能の筒抜けのクラスになっています。

②privateの代わりにprotected修飾子を使う

class Person {
  constructor(protected readonly name: string, public age: number) {}
  hello() {
    console.log(`hello ${this.name}`);
  }
}
const bob = new Person("Bob", 22); //生成されるインスタンスはオブジェクト
console.log(bob.name); //Bob//エラー(プロパティ 'name' は保護されているため、クラス 'Person' とそのサブクラス内でのみアクセスできます。)

class Japanese extends Person {
  //継承先 extends 継承元
  constructor(name: string, age: number, private gender: "male" | "female") {
    super(name, age); //superメソッドで継承元のプロパティを継承先でも使える状態にします。
    this.gender = gender; //継承元のプロパティを受け継いだあとにJapanese独自のプロパティを設定。
  }
  hello() {
    console.log("konnichiwa" + this.name); //←継承元のメソッドを上書きすることができます。
  }
}

const takashi = new Japanese("Takashi", 24, "male"); //生成されるインスタンスはオブジェクトです(しつこいようですが…)

①の欠点を補うため、今回のケースではprotected修飾子を使うことでクラス外からのアクセスを制御し、また継承先(Japaneseクラス)でnameプロパティを使えるというコードに修正できました。

staticメソッド&プロパティ

classの修飾子の一つにstaticがあります。newをしてインスタンス化せずに直接クラスにアクセスすることでメソッドやプロパティを使える修飾子です。因みに、staticは静的と訳されて、静的メッソドや静的プロパティとも呼ばれます。
ここではPersonクラスをいじり、staticの使い方を見てみましょう。

class Person {
  static birthdate = "1998/01/01";
  static legLength(leg: number) {
    return { length: leg };
  }
  constructor(protected readonly name: string, public age: number) {}
  hello() {
    console.log(`hello ${this.name}`);
  }
}
console.log(Person.birthdate);//1998/01/01
console.log(Person.legLength(27));//{length: 27}

このようにインスタンス化せずにクラス内にアクセス可能となります。自分でクラスを作って、static修飾子を使ってクラス内にアクセスするというケースはあまり機会がないかと、僕個人としては思います。