TypeScript クラスの定義

TypeScript クラスの定義

いまさらながらTypeScriptに入門

2022-01-29TypeScript

クラスのの定義方法あれこれ

  • class クラス名{}で定義
  • プロパティはthis.変数名で定義
  • コンストラクタはconstructor(パラメータ)で定義。

    コンストラクタ内でプロパティを初期化しないとエラーが発生してしまう。インスタンス化の後で初期化をしたい場合、プロパティを「プロパティ名!:型名」で定義する。

  • アクセス修飾子public、protected、privateが使える。デフォルトはpublic

    • public:クラスの外からも自由にアクセスできる
    • protected:同じクラス内、派生クラスからアクセスできる
    • privete:同じクラスからのみアクセスできる
  • getter、setterを定義するとプロパティへの代入時に自動で呼ばれる。

    「set プロパティ名」、「get プロパティ名」で定義

  • 静定メンバー

    staticをつけて静的プロパティ、静的メソッドを定義できる

class Person {
  // 変数名定義(privateで定義)
  private _name:string;
  private _age:number;
  private _address !:string;//「!」をつけると任意となりコンストラクタで指定しなくてもOKとなる
  static DESC:string = 'PERSONクラスの静的プロパティです';//静的プロパティ

  // コンストラクタ定義
  constructor(name:string, age:number){
      this._name = name;
      this._age = age;
  }
  // 関数定義
  show():string{
      return `${this._name}${this._age}`;
  }
  // setter
  set address(address:string){
      this._address = `日本:${address}`;
  }
  // getter
  get address():string{
      return `[${this._address}]`
  }
  // 静的メソッド
  static description():string{
      return this.DESC;
  }
}

let p = new Person('テス川 テス男', 30);
p.address = '南方の島';//setterが実行される
console.log(p.show());//関数実行
console.log(p.address);//getterが実行される。()はいらない
console.log(Person.description());//静的メソッド実行


/***
 * 出力結果
テス川 テス男は30歳
[日本:南方の島]
PERSONクラスの静的プロパティです
 * /

名前空間

名前空間が別なら同じ名前のクラス名を定義できる。

/***
 * 異なる名前空間ごとにPersonを定義する
 */

class Person{
    private _name:string;
    constructor(name:string){
        this._name = name;
    }
  }

  /**
   * 名前空間を作成し同じ名前のクラスを定義できるようにする
   */
  namespace AnotherWorld  {
    /** 別の名前空間からアクセスできるようにexport */
    export class Person{
        private _name:string;
        constructor(name:string){
            this._name = name;
        }
      }

    /** 名前空間をネスト */
    export namespace Hell{
      /** 別の名前空間からアクセスできるようにexport */
      export class Person{
        private _name:string;
        constructor(name:string){
            this._name = name;
        }
      }
    }
  }

    // 「.」でもネストできる
  namespace AnotherWorld.Heaven{
    export class Person{
        private _name:string;
        constructor(name:string){
            this._name = name;
        }
      }
  }

  let per1:Person = new Person('ジョン');
  let per2:AnotherWorld.Person = new AnotherWorld.Person('別世界のジョン');
  let per3:AnotherWorld.Hell.Person = new AnotherWorld.Hell.Person('地獄のジョン');
  let per4:AnotherWorld.Heaven.Person = new AnotherWorld.Heaven.Person('天国のジョン');

継承

  • extendで別のクラスを継承できる
  • サブクラスから親のメソッド、プロパティにアクセスするには「super.名前」
  • サブクラスで同じスーパークラスのメソッド名のメソッドを定義するとオーバライドできる
  • メソッド、プロパティにabstractをつけると抽象クラスとして定義されサブクラスで実装しないとエラーなる(このときclassにもabstractをつける)。抽象クラスのインスタンスは作成できない
/**
 * 親クラスを定義
 * 抽象化メメソッドが定義されているのでabstractをつける
 */
abstract class Parent{
  protected _prop:string;
  constructor(prop:string){
      this._prop = prop;
  }
  show():string{
      return `${this._prop}`;
  }
  // 抽象化メメソッドを定義
  abstract ab():string;
}

class Child extends Parent{
  work():string{
      return `プロパティ:${this._prop}`;
  }
  //スーパークラスのメソッドをオーバライド
  show():string{
      return `オーバーライド:${super.show()}`;
  }
  // 抽象化メソッドのため実装しないとエラー
  ab():string{
      return 'abstractメソッド';
  }
}

const c = new Child('AAAAAA');
console.log( c.work());//プロパティ:AAAAAA
console.log( c.show());//オーバーライド:AAAAAA
console.log( c.ab());//abstractメソッド

インターフェース

  • クラスの継承だと1つのクラスしか継承できない、インターフェースは複数可能
  • 「interface インターフェース名」で定義し、「class クラス名 implementsインターフェース名 」で定義する
  • インターフェースは抽象メソッド、プロパティのみなので、継承先で実装する必要がある。
// インターフェス、intA、intBを定義
interface intA{
    show():string;
}
interface intB{
    description():string;
}

// intA、intBをimplementsで継承する
class classA implements intA,intB{
    // intAの実装
    show():string{
        return 'intAのメソッド';
    }
    // intBの実装
    description():string{
        return 'intBのメソッド';
    }
}
// intAを継承したclassBを定義
class classB implements intA{
    // intAの実装
    show():string{
        return 'intAのメソッド';
    }
}

// 型をintAで定義するとintAのメソッドのみ使用できる
let valA:intA = new classA();
console.log(valA.show());
// valAはintA型なので、intAを継承しているclassBも代入できる
valA = new classB();

// 型をclassAで定義するとintA、intB両方のメソッドを使用できる
const valB:classA = new classA();
console.log(valB.description());

// 型classBにclassAの代入するとintAのメソッドのみ使用できる
const valC:classB = new classA();
console.log(valC.show());

// classA型にはclassBは代入できない
// const valD:classA = new classB();

this型

  • メソッドの戻り値の型を「this」(自分自身)にしてメソッドの結果からメソッドを実行することができる
class classA{
  private _str:string;
  constructor(str:string){
      this._str = str;
  }
  get value():string{
      return this._str;
  }
  //trimを実行してthis(自分自身)を返す
  trim():this{
      this._str = this._str.trim();
      return this
  }
  // toUpperCaseを実行してthis(自分自身)を返す
  upper():this{
      this._str = this._str.toUpperCase();
      return this;
  }
}

const valA = new classA('  aaBbCC  ');
console.log(valA.trim().upper().value);//AABBCC