TS(js)におけるクラスとか
2021-08-10

クラス

graphQLが型を作るので、自身はあまり使ったことはないが、jsでも、クラスの概念を利用することができる。

class Coordinate {
    x: number;
    y: number;
    constructor(x: number, y: number) {
        this.x = x;
        this.y = y;
    }
    add(coordinate: Coordinate) {
        return new Coordinate(this.x + coordinate.x, this.y + coordinate.y);
    }
}

const y10 = new Coordinate(0, 10);
const x10y20 = new Coordinate(10, 20);
const customCoordinate = p1.add(p2); 
// customCoordinateの中身:
// Coordinate: {
//   "x": 10,
//   "y": 30
// } 

constructorを用いて、初期化を行う。

継承(Ingeritance)

Typescriptでも、extendsキーワードを用いて、単一継承をサポートしている。

class zCoordinate extends Coordinate {
    z: number;
    constructor(x: number, y: number, z: number) {
        super(x, y);
        this.z = z;
    }
    add(coordinate: zCoordinate){
        const _coordinate = super.add(coordinate);
        return new zCoordinate(_coordinate.x, _coordinate.y, this.z + coordinate.z)
    }
}

クラスにコンストラクタが存在する場合、コンストラクタから親のコンストラクタを呼ぶ必要がある。(super(x, y)) これを記載しないと、TypeScriptではエラーになる。この記述により、thisのプロパティが正しく設定される。 superを実行したあと、コンストラクタで必要な処理を追加することが出来る。(今回ではz座標としてプロパティzを追加した)

また、親のメンバ関数をオーバーライドする場合でも、親機能を呼び出すと良いです。(こちらはsuper使わなくても別にエラーにはならないので、全く別処理ならsuperしなくてもいい)

静的メンバ(Statics)

TypeScriptクラスは、クラスの全インスタンスで共有されるstaticなプロパティをサポートしている。静的メンバに対してアクセスする自然な場所はクラス自体。 プロパティだけではなく、静的関数もサポートしている。

Railsでいうところの定数みたいな使い方ができる

アクセス修飾子

TypeScriptは、アクセス修飾子として、 public private protected をサポートしている。 これらは、classメンバのアクセスを制御する。

対象 public protected private
クラス yes yes yes
継承先のクラス(子クラス) yes yes no
クラスインスタンス yes no no

何もアクセス修飾子を書かない場合は、public扱いになる。

Rails でも似たようなprivateとかが有るので、馴染みやすいと思う。

Abstract修飾子

abstractを使用すると、そのクラスは、抽象クラスになる。抽象クラスは、直接インスタンス化することはできません。 また、メンバに対してabstract修飾子を用いた場合、継承先のクラスは、その機能を定義する必要があります。

これを用いることによって、継承先のクラスがどんなメンバを用意しなくてはいけないのか制約をかけることができます。

コンストラクタは必須ではないですが、メンバを定義し、初期化するためには、コンストラクタを使用する。

また、コンストラクタの引数、初期化構文は同名の場合、省略が出来る。

class Foo {
    constructor(public x:number, public y: number) {
    }
}

このように記載することで、Fooクラスには、プロパティとしてx,yが利用できるようになる。

プロパティ初期化子

ES7から利用できる機能。 クラスのコンストラクタの外で、クラスのメンバ変数を初期化出来る。

type Licence = {
  subject: string
  getDate: string
}

class Guest {
    licences: Licence[] = [];

    constructor(public name?: string, public age?: number){
    }
}

const guest = new Guest('スズキ', 23)
const defaultLicence: Licence = {subject: '漢検', getDate: '2020/02/02'}
guest.licences.push(defaultLicence)
    

とかが出来る。

便利なPartial

TypeScript2.1から導入されているtypeで、Partial<T>と指定した場合に、Tが持つすべてのプロパティをオプションとした型として振る舞う性質を持っている。 つまり

class User {
  name?: string;
  age?: number;
  constructor(init: Partial<User>){
    Object.assign(this, init)
  }
}

このようにすることで、initは、


{ name?: string, age?: number }

の形になります。継承した場合も、

class Doctocr extends User {
  role?: string
  constructor(init: Partial<Doctocr>){
    super(init)
  }
}

という記述でDoctorクラスは、name, age, roleの3つのプロパティを初期化したインスタンスを作成することができます。

ちなみに、 Object.assign(target, source); は、targetがコピー先オブジェクトになるので、個々の値に、soruceをコピーする形になる。