TypeScriptのクラスについてまとめる

study フロントエンド

TypeScriptでクラスを使う機会が増えてきたので、改めてクラスについて整理します。

TypeScript Deep Diveのクラスのページを参考に、試しながらまとめてみたいと思います。

今回は練習として、飲み物クラスと紅茶クラスを作ってみます。

クラス

簡単なクラス定義

飲み物クラスDrinkを作りました。

type Temperture = 'hot' | 'cold';

class Drink {
    temperature: Temperture;
    constructor(temperature: Temperture) {
        this.temperature = temperature;
    }
}

const hotDrink = new Drink('hot');
console.log(hotDrink.temperature);// hot

クラスのメンバ変数として温度temperatureを設定できるようにしました。
あったかい飲み物が作れました。

コンストラクタの省略

コンストラクタは省略可能です。
なので温度のメンバ変数を作らず、単にDrinkクラスを作ることができます。

メンバ定義の省略

また、メンバ変数の定義を省略することができます。
コンストラクタの引数にアクセス修飾子(後述)を指定でき、自動的にクラス内に宣言され、値もコンストラクタの引数によって初期化されます。

以下の記述で、最初のクラス定義と同様の内容となります。

type Temperture = 'hot' | 'cold';

class Drink {
    constructor(temperature: Temperture) {} // 代入しなくて良い
}

const hotDrink = new Drink('hot');
console.log(hotDrink.temperature);// hot

継承

extendsを使うことで継承ができます。
飲み物クラスを継承して紅茶クラスを作ってみます。

class Tea extends Drink {
    constructor(public temperature: Temperture, public brand: string) {
        super(temperature);
    }
}

const tea = new Tea('hot', 'assam');
console.log(tea.temperature); // hot
console.log(tea.brand); // assam

継承したクラスにconstructorをつける場合は、コンストラクタで継承元(親)コンストラクタを呼び出す必要があります。

superで親コンストラクタのメンバ変数temperatureをオーバーライドすると、子クラスで新たな処理を追加することができます。

今回は紅茶の銘柄を設定できるようにしました。
あったかいアッサムティーができました。

静的メンバ

staticプロパティのメンバ変数は、クラスの全てのインスタンスで共有されます。
クラス名.メンバ名でアクセスできます。

試してみましょう。

type Temperture = 'hot' | 'cold';

class Drink {
    static num: number = 0;
    constructor(public temperature: Temperture) {
        Drink.num++;
    }
}

class Tea extends Drink {
    constructor(public temperature: Temperture, public brand: string) {
        super(temperature);
    }
}

const tea1 = new Tea('hot', 'assam');
const tea2 = new Tea('cold', 'darjeeling');
console.log(tea1.brand); // assam
console.log(tea2.brand); // darjeeling
console.log(Tea.num); // 2
console.log(Drink.num); // 2

飲み物クラスに生成された数numメンバを追加しました。
コンストラクタで毎回プラスされます。
紅茶クラスは飲み物クラスを継承しているのでnumにアクセスすることができます。

あたたかいアッサムティーと冷たいダージリンティーを作りました。
それぞれ飲み物としては別物ですが、飲み物クラスのインスタンス数がクラス内で共有されるので、飲み物は2つ、となりますね。

アクセス修飾子

TypeScriptではpublicprivateprotectedがサポートされています。
メンバ変数とメンバ関数に使用することができます。

publicはどこからでもアクセス可能です。
同一クラス、クラスから継承した子クラス、インスタンスとなってもアクセスができます。

privateは同一クラス内でのみアクセス可能です。

protectedは同一クラス、クラスから継承した子クラスはアクセスできますが、インスタンスではアクセスできません。

アクセス修飾子が指定されていない場合は、暗黙的にpublicとなります。

試してみましょう。

type Temperture = 'hot' | 'cold';

class Drink {
    name: string = 'hoge';
    private capacityMl: number = 500;
    protected price: number = 600;
    static num: number = 0;
    constructor(public temperature: Temperture) {
        Drink.num++;
        console.log('Drink:', this.name);
        console.log('Drink:', this.capacityMl);
        console.log('Drink:', this.price);
    }
}

class Tea extends Drink {
    constructor(public temperature: Temperture, public brand: string) {
        super(temperature);
        console.log('Tea:', this.name);
        console.log('Tea:', this.capacityMl); // プロパティ 'capacityMl' はプライベートで、クラス 'Drink' 内でのみアクセスできます。ts(2341)
        console.log('Tea:', this.price);
    }
}

const drink1 = new Drink('hot');
console.log('drink1:', drink1.name);
console.log('drink1:', drink1.capacityMl); // プロパティ 'capacityMl' はプライベートで、クラス 'Drink' 内でのみアクセスできます。ts(2341)
console.log('drink1:', drink1.price); // プロパティ 'price' は保護されているため、クラス 'Drink' とそのサブクラス内でのみアクセスできます。ts(2445)
const tea1 = new Tea('hot', 'assam');
console.log('tea1:', tea1.name);
// console.log('tea1:', tea1.capacityMl); // プロパティ 'capacityMl' はプライベートで、クラス 'Drink' 内でのみアクセスできます。ts(2341)
console.log('tea1:', tea1.price); // プロパティ 'price' は保護されているため、クラス 'Drink' とそのサブクラス内でのみアクセスできます。ts(2445)

飲み物クラスに
publicメンバ変数name、privateメンバ変数capacityMl、protectedメンバ変数priceを追加しました。
飲み物の名前、容量、値段、を設定できるイメージです。


まず、飲み物クラスのコンストラクタ内で全てのメンバ変数にアクセスしたところ、全てアクセスできました。


次に、紅茶クラスのコンストラクタ内で全てのメンバ変数にアクセスしたところ、capacityMl はアクセス時にエラーとなりました。
privateなので親クラス以外からはアクセスできないのがわかります。


最後に飲み物クラスと紅茶クラスをインスタンス化したものからアクセスしました。
どちらもcapacityMl protectedはアクセス時にエラーとなりました。
先ほど同様、capacityMl privateなので親クラス以外からはアクセスできないのがわかります。
また、priceprotectedなので、子クラスではアクセスできますが、インスタンスからはアクセスできないことがわかります。


また、全てのパターンでnameにアクセスができたので、暗黙的にpublicになっていることがわかりました。

実際に動かしてみるとよくわかりますね。


まとめ

TypeScriptでクラスについてまとめていきました。
抽象クラスについてはまた後日描いてみたいと思います。

参考

TypeScript Deep Diveのクラス

コメント

タイトルとURLをコピーしました