【TypeScript | JavaScript】Classまとめ

# はじめに

最近ちゃんと`TypeScript`について勉強し直しまして、
`Class`を今までちゃんと使ってこなかったなあと思い、記事にしました。

同じような方の参考になれば幸いです。
`Class`の説明をする前に**オブジェクト指向**について理解しておく必要があるので、簡単に説明します。

## オブジェクト指向とは

話題の`BingAI`にお願いしました

:::note info
オブジェクト指向とは、プログラミングの考え方の一つで、現実世界に存在するものを「オブジェクト」として扱い、その特徴や機能を定義することです。オブジェクト指向には、以下のようなメリットがあります。

* オブジェクトを再利用しやすくすることで、開発効率や保守性を高めることができます。
* オブジェクト同士の関係を明確にすることで、設計や理解がしやすくなります。
* オブジェクトの内部構造や動作を隠蔽することで、安全性や信頼性を向上させることができます。
:::

わかりやすいですね。ここで具体例も紹介しておきます。

### 具体例

とある商品を題材に考えてみます。

その商品には以下のプロパティ(特徴・性質)があるとします。
1. String型の名前
1. number型の価格
1. 名前を変更することができる。
1. 商品をカートに入れることができる。

この場合、TypeScriptでは以下のようにオブジェクトを定義することができます。

```typescript
const goods_A = {
  name: 'A商品',          // 名前
  price: 100,            // 価格
  rename(n: string): void {    // 名前変更
    this.name = n;
  },
  addToCart(): void {          // カートに入れる
    cart.push(this.name)
  }
}

goods_A.name
// 'A商品'

goods_A.rename('a商品')
goods_A.name
// 'a商品'
```

このようにオブジェクトとして定義し、そのプロパティに「名前」や「価格」や、
「名前変更」や「カートに入れる」といった機能を定義していくプログラミング手法を**オブジェクト指向**と言います

# 1. classについて

先ほどの例から考えてみます。

以下のように商品Aがあります。

```typescript:typescript
const goods_A = {
  name: 'A商品',
  price: 100,
  rename(n: string): void {
    this.name = n;
  },
  addToCart(): void {
    cart.push(this.name)
  }
}
```

商品が複数件ある場合、

```typescript:typescript
const goods_B = {
  name: 'B商品',
  price: 100,
  rename(n: string): void {
    this.name = n;
  },
  addToCart(): void {
    cart.push(this.name)
  }
}

const goods_C = {
・
。
・
```

全ての商品で同じ動きをさせるためには作成分のオブジェクトを定義する必要が出てきますが、
こんなことはしてられません。

オブジェクトの設計書みたいなものがあれば、それを元に増やすことができそうです。
そのオブジェクトの設計書が**Class**です

先ほどの商品の設計書を`Class`を使い定義し、商品のオブジェクトを生成します。

:::note info
ちなみに、クラスから作成したオブジェクトをインスタンスと言います。
:::

**クラスからオブジェクト生成し、解読していきます。**

```typescript:typescript
// クラス定義
class Goods {
  name: string;
  price: number;

  constructor(n: string, p: number) {
    this.name = n;
    this.price = p;
  };

  rename(n: string): void {
    this.name = n
  };

  addToCart(): void {
    cart.push(this.name)
  };
}

// インスタンス生成
const goods_A = new Goods('商品A', 100)
goods_A.name
// 'A商品'
```

# 2. constructor関数とは

**constructor関数**とは、クラスで作成されたオブジェクトの初期化のためのメソッドです。
**constructor関数**は、クラスが呼び出された際に最初に実行されます。

つまり、`new Hoge()`の際に実行されるメソッドです。

```typescript:class
  name: string;
  price: number;

  constructor(n: string, p: number) {
    this.name = n;
    this.price = p;
  };
```

先ほど作成したクラスを見ると、
`name`を`string`型で定義、
`price`を`number`で定義し、
**constructor関数**で引数で受け取った値を初期値として格納しています。

```typescript
new Goods('商品A', 100)
```

このようにオブジェクトを生成します。
試しに引数に渡している値を変更し、`name`に`number`型をを入れてみます。

![スクリーンショット 2023-03-10 21.17.03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/b44cfc6f-1777-e8e3-ab6b-ca04bf29b5fa.png)

このようにエラーになります。
**constructor**関数の引数の型定義`(n: string, p: number)`が、
インスタンス化する時に作用していることが確認できますね。

# 3. 【TypeScriptのみ】 アクセス修飾子

TypeScriptでは、プロパティのアクセシビリティを制御するため、クラスのプロパティに対してアクセス修飾子`public` `private` `protected`を使うことができます。

```typescript:typescript
class Goods {
  owner: string; // 自動的にpublicになります。
  private name: string;
  public price: number;
  protected id: number;
}
```

* public
    * デフォルトはこれ。何も記載がないとpublicになります。
    * どこからでもアクセスできる
* private
    * 同じクラス内からのみアクセスできる
* protected
    * 同じクラスとサブクラス内からのみアクセスできる

## なぜ必要なのか

**不用意に値を変更できないようにするため**です。

次の例で説明します。

```typescript:typescript
class Goods {
  public name: string;

  constructor(n: string) {
    if (n.length < 100) {
      this.name = n;
    } else {
      this.name = '100文字以上'
    }
  };

  rename(n: string): void {
    if (n.length < 100) {
      this.name = n
    }
  };
}
```

このように100文字以下のバリデーションがついた`name`プロパティがあったとします。

### アクセス修飾子が`public`の場合

`public`のプロパティはどこからでもアクセスできるため、
以下のように、`rename`メソッドを通しても通さなくても値を変更できてしまいます。

```typescript:typescript
const goods_A = new Goods('商品A')

// 1. renameメソッドを通す
goods_A.rename('hoge')

// 2. 直接値を変更
goods_A.name = 'fuga'
```

`rename`メソッド内では100文字のバリデーションがかかるのに対し、
直接変更する場合はバリデーションをかけることができません。

### アクセス修飾子が`private`の場合

`private`のプロパティは同じクラス内からのみアクセスできるため
以下のように直接指定はできません

![スクリーンショット 2023-03-11 23.50.22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2985550/05b2a1d5-1c6d-2083-7771-0585ad7ef501.png)

この例で**不用意に値を変更しないように**アクセスプロパティを適切につける意味が理解できたと思います。


# 4. 【TypeScriptのみ】 constructor関数の省略記法

さて、次のコードを見て何を思いますか？

```typescript:typescript
class Goods {
  name: string;
  private price: number;
  private store: string;
  type: string;

  constructor(n: string, p: number, s: string, t: string) {
    this.name = n;
    this.price = p;
    this.store = s;
    this.type = t;
  };
}
```

私であれば**冗長**だなぁ...と思います。
TypeScriptであればこの冗長を解決できます。
それがこちらです

```typescript:typescript
class Goods {
  constructor(
    public name: string,
    private price: number,
    private store: string,
    public type: string
  ){};
}
```

このように初期化処理をかなりシンプルに記述できます。

`constructor`関数の引数にアクセス修飾子とプロパティ名を書くだけで自動で初期化処理まで行ってくれます。

:::note warn
アクセス修飾子は必ずつける必要があります。
publicであっても省略することはできません。
:::

ちなみにこのコードを`JavaScript`にコンパイルするとこうなります。

```javascript:javascript
class Goods {
  constructor(n, p, s, t) {
    this.name = n;
    this.price = p;
    this.store = s;
    this.type = t;
  };
}
```

アクセス修飾子がなくなり、全て`constructor`関数でthisの中に格納する形になっていますね。

# 5. まとめ

:::note info
1. オブジェクト指向とは、現実世界に存在するものを「オブジェクト」として扱い、その特徴や機能を定義すること。
1. classとは、オブジェクトの設計図のようなもの。
1. constructor関数とは、クラスで作成されたオブジェクトの初期化のためのメソッド。
1. TypeScriptでは、不用意に値を変更できないようにするため、クラスのプロパティに対してアクセス修飾子`public` `private` `protected`を使い、アクセスの制限をかけることができる。
1. TypeScriptでは、初期化処理を簡潔にかける省略記法が存在する
:::

他にも`interface`を使ってclassを実装する方法だったり、継承だったり、説明できることはたくさんありますが、今回はここまでの基本的な使い方までとさせていただきます。

続きについてはまた今度書きたいと思います。