見出し画像

TypeScript 入門の記録(58)プロを目指す人のためのTypeScript入門(42)第5章TypeScriptのクラス(2)

今週は、「第3回 CPU+コンパイラ自作ワークショップ 基盤製作デー 」の開催サポートのため、東京出張しています。 前回から、第5章「TypeScriptのクラス」の学習中です。今日もTypeScriptのクラスについて学習します💪

TypeScriptのクラスの続き

今日は、最近気になっていた GitHub Codespaces で TypeScript 入門のコーディング環境を作って、そこで学習を進めます。

GitHub Codespaces の環境構築

クイックスタートに従ってテンプレートを利用して、CodespacesにNode.jsの環境がサクッとできるので、TypeScriptの環境にします。
下図は、nodeがインストール済であることを確認して、TypeScriptをインストールして、インストールできたTypeScriptのバージョンを確認したところです。

Node.jsのバージョンを確認してTypescriptのコンパイラをインストールしてバージョン確認できた様子
tsc --init

TypeScriptのプロジェクトを初期化して生成された tsconfig.json を開いて、コンパイル後の出力先を "./dist" に修正しました。これでコンパイル後のjsは ./dist/配下に出力されます。

tsconfig.jsonの ”outDir" を "./dist" に修正して保存

よし、準備ができました。先週の学習の続きから再開です。

静的プロパティ・静的メソッド

class User に、静的プロパティと静的メソッドの例に倣って、コードを追加してみます。

// 静的プロパティ・静的メソッド
class User {
    static adminName: string = "uhyo";
    static getAdminUser() {
        return new User(User.adminName, 26);
    }

    name: string;
    age: number;
    constructor(name: string, age: number) {
        this.name = name;
        this.age = age;
    }

    readonly ADULTAGE:number = 20;
    isAduult(): boolean {
        return this.age >= this.ADULTAGE;
    }
}

console.log(User.adminName);    // "uhyo" が表示される
const admin = User.getAdminUser();
console.log(admin.age);         // 26 が表示される
console.log(admin.isAduult());  // true が表示される
const uhyo = new User("uhyo", 26);
//console.log(uhyo.adminName); ←コンパイルエラーなのでコメントアウト
// 静的プロパティ・静的メソッド class User {     static adminName: string = "uhyo";     static getAdminUser() {         return new User(User.adminName, 26);     }      name: string;     age: number;     constructor(name: string, age: number) {         this.name = name;         this.age = age;     }      readonly ADULTAGE:number = 20;     isAduult(): boolean {         return this.age >= this.ADULTAGE;     } }  console.log(User.adminName);    // "uhyo" が表示される const admin = User.getAdminUser(); console.log(admin.age);         // 26 が表示される console.log(admin.isAduult());  // true が表示される const uhyo = new User("uhyo", 26); を保存して、node ./dist/index5-1.jsの実行結果がuhyo,26,trueが出力された
実行してみると、uhyo,26,trueが表示された

ここで、adminNameは静的プロパティなのにインスタンスのプロパティとしてアクセスしようとすると…

console.log(user.adminName)と書くと、プロパティ'adminName'は型'User'には存在しません。代わりに静的メンバー'User.adminName'にアクセスしようとしていましたか?というエラーが表示される
静的プロパティをインスタンスのメンバーとしてアクセスしようとするとエラー

普段のVSCodeで実行しているときと同様にエラーも表示されますね。これは、良いですね~。

3種類のアクセシビリティ修飾子

クラス宣言内のプロパティとメソッドには、public, protected, private の3種類のアクセシビリティ修飾子がつけられます。

コード、// 3種類のアクセシビリティ修飾子 class User {      name: string;     private age: number;     constructor(name: string, age: number) {         this.name = name;         this.age = age;     }      readonly ADULTAGE:number = 20;     public isAduult(): boolean {         return this.age >= this.ADULTAGE;     } }  const uhyo = new User("uhyo", 26); console.log(uhyo.name);         // uhyo が表示される console.log(uhyo.isAduult());   // true が表示される //console.log(uhyo.age);のコンパイルと実行結果を確認するとuhyo,trueと表示された
プロパティ宣言、メソッド宣言でアクセシビリティ修飾子を使用した様子

コンストラクタ引数でのプロパティ宣言

さらに、コンストラクタ引数でそのままプロパティの初期化をする際に、アクセシビリティ修飾子も付けることができます。

// コンストラクタ引数でのプロパティ宣言
class User {

    constructor(public name: string, private age: number) {
        this.name = name;
        this.age = age;
    }

    readonly ADULTAGE:number = 20;
    public isAduult(): boolean {
        return this.age >= this.ADULTAGE;
    }
}

これは、かなりスッキリしますね!これは頻出らしいので、サクッと書けるようになりたいです!

クラス式でクラスを作成する

クラス宣言ではなく、クラス式でもクラスを作成できます。
これも、何となくTypeScriptらしさを感じます。ところで、テキストには「クラス式の中では、privateやprotectedのプロパティが使用不可能である」と書かれているのですが、コンストラクタ引数で定義する場合は正常にコンパイルと実行ができたので、やりようはあるということかなと思いました。

// クラス式でクラスを作成する
const User = class {

    constructor(public name: string, private age: number) {
        this.name = name;
        this.age = age;
    }

    readonly ADULTAGE:number = 20;
    public isAduult(): boolean {
        return this.age >= this.ADULTAGE;
    }
}

const uhyo = new User("uhyo", 26);
console.log(uhyo.name);         // uhyo が表示される
console.log(uhyo.isAduult());   // true が表示される

もう1つのプライベートプロパティ

TypwScriptでは、他のプログラミング言語と同様に private 修飾子でプライベートなプロパティを宣言できますが、 ”#プロパティ名” のように先頭に#を付けることで、そのクラス内のみアクセス可能なプロパティを宣言することができます。
class User のプロパティ age を、#age に置き換えてみます。

class User のプロパティageを、#ageに置き換え // もう1つのプライベートプロパティ class User {      name: string;     #age: number;      constructor( name: string, age: number) {         this.name = name;         this.#age = age;     }      readonly ADULTAGE:number = 20;     public isAduult(): boolean {         return this.#age >= this.ADULTAGE;     } }  const uhyo = new User("uhyo", 26); console.log(uhyo.name);         // uhyo が表示される console.log(uhyo.isAduult());   // true が表示される 実行結果は、Uhyo,true で、想定どおりだった
プロパティの先頭に#を付けることでプライベートプロパティ宣言ができる

この、#プロパティ名 という形式は馴染みがないのですが、ES2015以上で使用可能だそうです。
新しい言語仕様は便利だったり安全性が高まったりしているので、すぐに使いたくなりますが、関連モジュールとの影響を考えると簡単に最新を使えるわけではないので、バランスが難しそうです。(これは、TypeScriptに限った話ではないですね~)

クラスの静的初期化ブロック

比較的最近追加された機能に、静的初期化ブロックがあります。別名 static ブロック とも呼ばれます。
クラス宣言の中に static { … } という構文が書けるというものです。static ブロックは「クラスの中」とみなされるので、static ブロック内ではプライベートプロパティにアクセスができます。本来、プライベートプロパティは、getter と setter でのみアクセスできるように制限するような使い方をします。しかし、static ブロックを使用することで、この制約を突破できるのです。ええええええ!?
実際に試してみます。

// クラスの静的初期化ブロック class User {     static adminUser: User;     static {         this.adminUser = new User();         this.adminUser.#age = 9999;     }     #age: number = 0;      getAge() {         return this.#age;     }      setAge(age: number){         if (age < 0 || age > 150){             return         }         this.#age = age;     } } console.log(User.adminUser.getAge());   // 9999 と表示される とすることで、ageプロパティに設定可能な範囲を越える年齢が設定できてしまう
0 ~ 150 の範囲を超える age が設定できる例

#age には、 #age > 0 かつ、#age < 150 の範囲内の値しか登録できない条件なのに、this.administrator.#age = 9999; が成立します。

型引数を持つクラス

クラスは型引数を持つことができます。

// 型引数を持つクラス
class User<T> {
    name: string;
    #age: number;
    readonly data: T;

    constructor(name: string, age:number, data: T){
        this.name = name;
        this.#age = age;
        this.data = data;
    }

    public isAdult() : boolean {
        return this.#age >= 20;
    }
}

const uhyo = new User<string>("User", 26, "追加データ")
const data = uhyo.data;
const john = new User("Jhon Smith", 15, {num: 123});
const data2 = john.data;
console.log(data2.num);     // 123 が表示される

これをコンパイルして実行してみると、想定どおり 123 が表示されました。

実行結果は想定通り、123が表示された
型引数を持つクラスの例

まとめ

今日は、GitHub Codespaces を利用してTypeScriptのクラス宣言について学習しました。初めて Codespaces を使ったのですが、サッと環境が作れてコードを試すことができてステキだと思いました。Reactも試してみたいとか、他のプログラミング言語もチャレンジしてみたいとか、野望が膨らみました。たのしい。今日の学習は、ここまでにします。続きは、また次の週末の予定です。


いいなと思ったら応援しよう!