【Typescript】入門してみたまとめ ✍🏻

# TypeScriptとは

Javascriptにコンパイルされる静的型システムがついたJavascriptの上位集合。

**Javascriptにコンパイル**
[TypeScriptPlayground](https://www.typescriptlang.org/play?#code/PTAEHUFMBsGMHsC2lQBd5oBYoCoE8AHSAZVgCcBLA1UABWgEM8BzM+AVwDsATAGiwoBnUENANQAd0gAjQRVSQAUCEmYKsTKGYUAbpGF4OY0BoadYKdJMoL+gzAzIoz3UNEiPOofEVKVqAHSKymAAmkYI7NCuqGqcANag8ABmIjQUXrFOKBJMggBcISGgoAC0oACCbvCwDKgU8JkY7p7ehCTkVDQS2E6gnPCxGcwmZqDSTgzxxWWVoASMFmgYkAAeRJTInN3ymj4d-jSCeNsMq-wuoPaOltigAKoASgAywhK7SbGQZIIz5VWCFzSeCrZagNYbChbHaxUDcCjJZLfSDbExIAgUdxkUBIursJzCFJtXydajBBCcQQ0MwAUVWDEQC0gADVHBQGNJ3KAALygABEAAkYNAMOB4GRonzFBTBPB3AERcwABS0+mM9ysygc9wASmCKhwzQ8ZC8iHFzmB7BoXzcZmY7AYzEg-Fg0HUiQ58D0Ii8fLpDKZgj5SWxfPADlQAHJhAA5SASPlBFQAeS+ZHegmdWkgR1QjgUrmkeFATjNOmGWH0KAQiGhwkuNok4uiIgMHGxCyYrA4PCCJSAA)で試すとわかるが、左側のエディタでtypescriptを書くと、

```index.ts
const hello: string =  'hello'
```
右側のエディタにjsにコンパイルされたものが表示される

```index.js
const hello = 'hello';
```

**静的型システム**

TypescriptがJavascriptにコンパイルされる際に型検査を行うシステム。

**Javascriptの上位集合**

現行のJavaScriptで記述されたプログラムはすべて、TypeScriptのプログラムとして実行が可能。

## はじめに

### NodejsでTypescriptインストール

`npm install -g typescript`とターミナルに入力しインストール。
権限エラーが表示された場合、
`sudo npm install -g typescript`

### Javascriptにコンパイル

Typescriptをインストールできたら`tsc`というコマンドが使用できるようになり、`tsc Typescriptのファイル名`と入力すると、コンパイルされたjsファイルが自動生成される。

例)
ts拡張子のtypescriptファイルを作成しts記述

```index.ts
const hello: string = "hello";
console.log(hello);
```

ターミナルで`tsc index.ts`と入力するとコンパイルされjsファイルが生成。

```index.js
var hello = "hello";
console.log(hello);
```

### Typescriptを使うべき理由

1. 型を定義していく仕様上、半自動的にドキュメントのようなコードが書ける。
1. コンパイル時にエラー表示してくれる型付きLinterとしての側面がある。
1. ES5へのコンパイラ
    デフォルトだとES３にコンパイルされる。
    `tsc index.ts --target ES6`などと、コンパイルターゲットも指定できる。

<br>

## Typescriptの型はこう書く！

一般的には、変数、関数の仮引数、関数の戻り値など値に対して型をつける。

### boolean型、number型、string型

下記のように記述することでそれぞれの型を宣言できる。

```ts
const hasValue: boolean = true;
const count: number = 10; //整数も浮動小数点もマイナスも同じ
const single: string = "hello";
```

### 型注釈と型推論

下記のように`: boolean`などの型を記述する書き方を**型注釈**という。

```ts
const hasValue: boolean = true;
```

この型注釈を省力すると右の値から推測をして自動的に型を決めてくれる。
それを**型推論**という。

```ts
let value = 3.333;
```

vscodeではTypescriptが内蔵されていて、変数をhoverすると型を確認できる。
下記のように型推論によって、`value`はnumber型になっている。
型が異なる値を代入しようとするとエラー表示してくれる。
![スクリーンショット 2022-11-17 9.28.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/27837806-8ab3-f7be-ad7c-9b0032de7002.png)
![スクリーンショット 2022-11-17 9.29.16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/c61ae2bc-f674-ce4c-58d0-c76640934e92.png)

### オブジェクトに型をつける

普通に書くと型推論によって、プロパティごとに型を決めてくれる。

```ts
const person = {
  name: "jack",
  age: 26,
};
```

![スクリーンショット 2022-11-17 9.49.17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/d6e3e62e-8a31-90b7-c780-41a955eafb80.png)

型注釈で書いた場合

```ts
const person : {
  name: string; // セミコロンであることに注意
  age: number;
} = {
  name: "jack",
  age: 26,
};
```

###  配列に型をつけるArray型

型推論で書く

```ts
const fruits = ["apple", "banana", "grape"];
```

hoverしてみる
![スクリーンショット 2022-11-18 9.02.43.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/deeb9cb2-8e96-187e-af1f-18686502c3ec.png)

型注釈で書く

```ts
const fruits: string[] = ["apple", "banana", "grape"];
```

異なる型をpushしてみるとちゃんとエラー表示する
![スクリーンショット 2022-11-18 9.05.01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/33ee9d3e-d668-03a5-ac94-ae17b4702755.png)

異なる型を配列に格納したい場合

型推論で書くと、ユニオン型になり、二つの型を代入できるようになる。
> ユニオン型の型注釈は、2つ以上の型をパイプ記号(|)で繋げて書きます。
[サバイバルTypescript：ユニオン](https://typescriptbook.jp/reference/values-types-variables/union)
```ts
const fruits = ["apple", "banana", "grape", 1];
```
![スクリーンショット 2022-11-18 9.09.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/0abd082c-cb40-41a0-2934-78bedfb14e79.png)

型注釈の場合、上記ユニオン型以外にany型でも書ける。
> TypeScriptのany型は、どんな型でも代入を許す型です。プリミティブ型であれオブジェクトであれ何を代入してもエラーになりません。

[サバイバルTypescript：any](https://typescriptbook.jp/reference/values-types-variables/any)

```ts
const fruits: any[] = ["apple", "banana", 1];
```

### Tuple型

決まった内容の配列を作るときなどに使用するタプル型。
配列の強化版のようなイメージ。

> タプル型は、複数の値を保持することのできる型です。 [] 記号を使うところも配列によく似ていますが、それぞれの位置の要素の型を明示しておくことができます。

[Typescriptのタプル型を定義する](https://maku.blog/p/6uzqf4q/#:~:text=TypeScript%20%E3%81%AE%E3%82%BF%E3%83%97%E3%83%AB%E5%9E%8B%E3%81%AF,%E3%81%8A%E3%81%8F%E3%81%93%E3%81%A8%E3%81%8C%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82&text=%E4%B8%8A%E8%A8%98%E3%81%AE%E4%BE%8B%E3%81%A7%E3%81%AF%E3%80%812,%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%82%82%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%80%82)

タプル型を型注釈で書く。
```ts
const books: [string, number, boolean] = ['business', 1500, false]
```

異なる型を代入しようとしたり、指定した要素数以上に追加したりするとエラー表示。
※pushで追加した場合、エラー表示されない...その追加した要素に参照しようとするとエラーが出るが、途中経過までは見ないらしい🤔

![スクリーンショット 2022-11-18 9.44.32.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/9c45a675-83dd-36e5-99e5-b37011d6c8b0.png)
![スクリーンショット 2022-11-18 9.43.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/c5fcd6e7-fdb8-534b-aa66-85db112e2c39.png)
![スクリーンショット 2022-11-18 9.46.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/f08c2205-c48c-5c55-68bf-b791425edc37.png)

### Enum型

特定のまとまったグループのみを受け入れる型。

> Enum(列挙)型とは名前付きの定数を定義するためのもの。
定義にはenumを使用し、定数名はすべて大文字とする。

[【TypeScript】列挙型](https://b1san-blog.com/post/ts/ts-enum/)

例えば、`coffee.size`には特定の4つの文字列しか受け入れたく無い場合。

```ts
enum CoffeeSize  {
  SHORT = 'SHORT',
  TALL = 'TALL',
  GRANDE = 'GRANDE',
  VENTI = 'VENTI'
}

const coffee = {
  hot: true,
  size: CoffeeSize.TALL
}
```

coffee.sizeの型を確認するとCoffeeSizeの列挙型になっていることがわかり、この列挙型とは異なる型を受け入れなくなる。

![スクリーンショット 2022-11-19 15.18.41.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/b4d5bfb7-090c-6b66-20d9-f6e309eb571e.png)

従って、代入する場合は下記のように、CoffeeSize列挙型からしかできない。
```ts
coffee.size = CoffeeSize.SHORT
```

### any型

すでに挙げている通り、なんでも許容する型。
> TypeScriptのany型は、どんな型でも代入を許す型です。プリミティブ型であれオブジェクトであれ何を代入してもエラーになりません。

jsの世界に戻るようなものなので、正しくtsの型の恩恵を受けたいなら基本的にany型は使わない。

any型、型注釈
```ts
let anything: any =  true;
```

どんな値を代入してもエラーにはならない
```ts
anything =  'hello';
anything = [];
anything = {};
anything.aaaa = 'aaaa';
```

型定義してあったものにも代入できてしまう..なので基本any型は使用しない🤨
```ts
let sample = 'sample'; // 型推論により、string型
sample = anything
```

### Union型

複数の型を受け入れる型。or演算子のように`|`で受け入れたい型を繋げて記述する。

```ts
let unionType: number | string = 10;
unionType = 'hello';
unionType = 1
```

配列のUnion型の書き方。
下記のように書けば、number型、string型のみ受け入れる配列になる。
```ts
let unionTypes: (number | string)[] = [ 20, 'hello' ]
```

### Literal型

特定の決まった値のみを扱う型。
下記の場合、appleのリテラル型となり、apple以外は入れられなくなる。

```ts
let apple: 'apple' = 'apple'
```

string型を代入してもエラーになる。
![スクリーンショット 2022-11-19 18.15.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/f9c1f19f-f1a0-ad8b-b7cf-3b42fc6d2ce6.png)

数値の場合も同様に、下記の変数`number`は1以外を代入できない
```ts
let number: 1 = 1
number = 0 // エラー
```

型推論で書く場合、`const`を使用した時点でリテラル型になる。
```ts
const apple = 'apple' // 型推論により、リテラル型になる
```

特定の値しか受け入れたく無い場合、このliteral型とunion型を組み合わせることで、enum型と似たようなやり方もできる。
下記の場合だと3つの文字列しか受け付けない変数になる。

```ts
let clothSize: 'small' | 'medium' |  'large' = 'small'
```

enum型との使い分けとすると、基本的にenum型の方が扱いやすい場合が多いので、enum型を使用し、逆に受け入れる値が少ない場合などは、literal型とunion型を組み合わせたやり方でもいいかもしれない。

**typeエイリアスとは**
型を変数のように扱える記述の仕方。
`type 変数名 =  値`という記述。letやconstと似ている。

```ts
type ClothSize = "small" | "medium" | "large"
let clothSize: ClothSize = "small";
```

### 関数に型を適応させる方法

関数は仮引数と、戻り値に型を適応させる。
戻り値の型は()の後に記述する。
戻り値に応じた型推論もできるが基本的に戻り値の型も型注釈したほうがいい。
仮引数の型は型推論できないので、必ず型注釈する。

```ts
// 関数宣言
function add(num1: number, num2: number): number {
  return num1 + num2;
}

// アロー関数1
const doubleNumber =  (num1: number): number =>  num1 * 2

// アロー関数2(関数を格納している変数に型注釈した記述)
const doubleNumber: (num1: number) => number = num1 =>  num1 * 2
```

**void型**

何も返さない型。下記は型推論で書いた例。
![スクリーンショット 2022-11-22 9.19.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/4a4967fc-a3b2-2a3b-36dd-23542ed3a65f.png)


### callback関数に型を適応

`cb: (num: number) => number`部分でコールバック関数の型注釈。
```ts
function doubleAndHandle(num: number, cb: (num: number) => number): void {
  const doubleNum = cb(num * 2);
  console.log(doubleNum);
}
doubleAndHandle(20, (doubleNum) => {
  return doubleNum;
});
```

### unknown型を使って、柔軟でany型より厳しい型を定義する方法

unknown型はany型のように、どんな型の値も代入できるが、any型と違って、
どんな値にも入れられるわけではない。
下記の例では
`text = unknownInput;`部分でエラーが出ていることがわかる。
![スクリーンショット 2022-11-22 20.33.18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/430e9ece-c24b-c13c-2d89-8adcfc1c9e50.png)

### never型

決して何も返さない型
例えば`new Error()`ではundefinedも何も返さないので、never型を使用する
```ts
function error(message: string): never {
  throw new Error(message);
}
console.log(error("This is an error"));
```

<br>

## コンパイラを使う方法

### watchモードで自動コンパイル

ターミナルで下記入力するとwatchモードになり、保存時に自動コンパイルされる。
`tsc index.ts -w`

### ファイルを一気にコンパイル

スペースで続けてファイル名を指定すると複数のコンパイルができる。
`tsc index.ts text.ts`

プロジェクトルートにtsconfig.jsonを設置することで`tsc`と入力するとtsconfig.jsonに設定されている内容に沿って、処理される。
デフォルトでは全てのtsファイルがコンパイルされる。

`tsc --init` → tsconfig.jsonが生成
`tsc` → 全tsファイルコンパイル
`tsc -w` → watchモード

### includeとexcludeを使ってコンパイルファイルを指定

tsconfig.jsonの`compilerOptions`プロパティの後などにincludeやexcludeプロパティを記載して、コンパイル対象を指定できる。
ワイルドカードが使用できる。

`"include": []` → コンパイルするファイルの指定。
`"exclude": []` → コンパイルしないファイルの指定。

`node_modules`はデフォルトではexcludeされているが、excludeを記述した場合は、`node_modules`も明記しないとコンパイルされてしまうので、注意。
```json
{
    "compilerOptions": {},
    "exclude": [
        "test.ts",
        "*.spec.ts",
        "tmp/compiler.ts"
        "**/compiler.ts"
        "node_modules"
    ]
}
```
`files`プロパティもあり、`include`と似ているが、こちらはワイルドカードが使用できないし、`exclude`より優先されるという違いがある。
デフォルトでは`include`も`files`も記述されていないので、全てのtsファイルがコンパイルされる。

### tsconfig.jsonのcompilerOptionsプロパティの解説

参考：
[tsconfig.jsonの全オプションを理解する](https://qiita.com/ryokkkke/items/390647a7c26933940470)

|オプション|説明|
|--|--|
|target|コンパイルターゲットの指定。`es5`などと記述する|
|lib|tsが用意する型の定義記述。指定されない場合はtargetに応じて自動的に判断されるので、基本的にこちらは記述しなくてok|
|allowJs|js拡張子をコンパイル対象にするかどうか。outDirなどと組み合わせて、コンパイルしたものを別のフォルダに格納したりする必要がある。|
|checkJs|単体ではtrueにできず、allowJsと一緒に使う。jsファイルもエラーチェックするかどうか。|
|jsx|reactjsの時に使用。|
|declaration|これをtrueにすると、コンパイルしたtsファイルの中でexportしているもの全ての型定義ファイルをファイルごとに作成する。|
|declarationMap|declarationオプションと併用するオプション。これをtrueにすると、型定義のmapファイルが作成される。|
|sourceMap|trueにして、コンパイルするとmapファイルが生成される。ブラウザでコードを見た際にtsファイルが見れるようになる。|
|outDir|tsがコンパイルされた際に生成するjsファイルの出力先を指定できる。`"outDir": "./dist"`のように記述するとdistディレクトリが生成され、その配下にコンパイルされたjsが生成される。|
|rootDir|コンパイル結果をoutDirで出力する際に、どのディレクトリ配下のディレクトリ構造で出力するかを指定する。|
|romoveComments|コメントを消すかどうか。trueにすると消える。|
|noEmit|何も出力しない。tsのチェックだけを行なって何も出力しない。|
|downlevelIteration|targetがes3とes5の時のみ使用できる。反復処理の際に正しく動作しなかったらtrueにしてみたりする。|
|noEmitOnError|trueにするとtsエラーが起こった際にjsファイルを生成しないようになる。|
|strict|このオプション自体は特定の機能を有効にするものではなく、このオプションをtrueにすると、下記の`noImplicitAny` ~ `alwaysStrict`の7つのオプションが全てtrueになる。|
|noImplicitAny|暗黙的にanyになる値をエラーにする。`strict`を`true`にした上で、任意のルールを一つずつ`false`にすることが可能。|
|strictNullChecks|Nullableな値に対してオプションの呼び出しを行う記述をエラーにする。|
|strictFunctionTypes|関数代入時の引数の型チェックにおいて、TypeScriptのデフォルトはBivariantlyな挙動だが、このオプションをtrueにするとContravariantlyに型チェックが走るようになる。|
|strictBindCallApply|bind, call, applyを使用する際に、より厳密に型チェックが行われるようになる。|
|strictPropertyInitialization|クラスを使用するときに使うもの。|
|noImplicitThis|使われているthisの型が暗黙的にanyになる場合にエラーにする。|
|alwaysStrict|`"use strict";`を必ず全てのファイルの先頭行に付与する。|

### コードの品質を保つ設定

|オプション|説明
|-|-|
|noUnusedLocals|宣言されたが使用されていない変数が存在する場合にコンパイルエラーにする。デフォルト値はfalse。とりあえずtrueにしておく。|
|noUnusedParameters|関数の作成時、定義しているのに中身のコードで使用されない場合にコンパイルエラーにする。これもとりあえずtrueにしておいて良さそう。|
|noImplicitReturns|暗黙的なreturnをエラーにするかどうか。関数内で、条件分岐の条件によって明示的なreturnがされないルートがある場合、コンパイルエラーになる。|
|noFallthroughCasesInSwitch|switch文を使用。fallthroughなcaseのうち、1行以上処理が存在しているにも関わらず脱出処理（breakやreturn）が無いものにエラーを吐く。trueにしておくと良さそう。

<br>

## TypescriptではClassをこう使う！

### オブジェクト指向プログラミング(OOP)とは

1. OOPとは現実世界の物に見立ててプログラムする方法
1. どのようにアプリケーションを作るかという方法の一つ
1. 人間にとってわかりやすくロジックを分割する方法の一つ

#### オブジェクトを作る方法はオブジェクトリテラルとClassの二つある

##### オブジェクトリテラル

```js
{
  name: {
    firstName: 'Peter',
    lastName: 'Quill'
  },
  age: 36,
  gender: 'male'
}
```

##### Class

1. オブジェクトの設計図
1. Claddから作られたオブジェクトはインスタンスと呼ばれる
1. 似たようなオブジェクトを複数作成する時に便利

### Classを定義してオブジェクト作成する方法

```ts
class Person {
  name: string; // フィールド
  constructor(initName: string) { // 初期処理
    this.name = initName;
  }
}
const quill = new Person("Quill");
console.log(quill); // → Person { name: 'Quill' }
```

### Classにメソッドを追加する方法

```ts
class Person {
  name: string;
  constructor(initName: string) {
    this.name = initName;
  }
  // メソッド追加
  greeting() {
    console.log(`Hello! My name is ${this.name}`);
  }
}
const quill = new Person("Quill");
quill.greeting(); // → Hello! My name is Quill
```

thisの使用には注意。
Tsは直接的な記述だったら面倒を見てくれるが間接的なものまでは見てくれない。
上記のquillインスタンスがある状態で、下記サンプル記述して確認。
```ts
const anotherQuill = {
  anotherGreeting1: quill.greeting, // nameはないのにエラー表示されない
  anotherGreeting2() {
    console.log(`Hello! My name is ${this.name}`); // 直接的に記述した場合はエラー表示してくれる
  }
};
anotherQuill.anotherGreeting1(); // → Hello! My name is undefined
```
![スクリーンショット 2022-11-23 17.10.08.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/e2c12646-5e29-c375-7eb7-3c8168fcf94e.png)

`anotherGreeting1`の型情報を確認するとvoidのみ。
![スクリーンショット 2022-11-23 17.17.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/cc89e625-bc80-8ba6-56c1-415b967fc3fb.png)

**Tsにメソッド内でthisを使用しているかどうか伝える方法**

メソッドの第一引数に`this: {プロパティ: 型}`を追記すると型情報が追加される。
`calss Person`のgreetingメソッドに追記
```ts
 greeting(this: { name: string }) {
    console.log(`Hello! My name is ${this.name}`);
  }
```
`anotherGreeting1`の型情報が追加されて、`anotherQuill.anotherGreeting1();`のメソッド実行部分でもエラー表示されるようになったことがわかる。
このようにすることで間接的な状態でもメソッドの中でthisが使用されているかどうかと、そのthisの情報を確認、エラー表示してくれるようになる。
![スクリーンショット 2022-11-23 17.21.58.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/4d304eda-93a4-2adf-eeb3-ab2e2fc20cb1.png)

**Classは型として使用できる**

TsではClassを作った場合、そのClassが作り出すインスタンスを表す型も同時に作っている。
`greeting(this: { name: string })`部分にPerson型を指定して、より厳格で安全なコードになる。
```ts
greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}`);
  }
```

### public修飾子とprivate修飾子でアクセス制限

classのフィールドやメソッドにはpublic修飾子とprivate修飾子というものが使用できる。
接頭辞としてつけることで使用でき、アクセス制限をかけられる。
接頭辞を何もつけないとデフォルトでは、public修飾子になり、どこからでもアクセス可能。

例）
`class Person`にageフィールドを追加。
```ts
class Person {
  name: string;
  age: number; // → 追加。接頭辞をつけていないので、public修飾子。
  constructor(initName: string, initAge: number) { // → 追加
    this.name = initName;
    this.age = initAge; // → 追加
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
  }
}
const quill = new Person("Quill", 38);
quill.age = 50; // → classの外側からアクセスでき、変更可能。
```
`quill.age = 50;`のようにclassの外側からいくらでもアクセスできる。
private修飾子をフィールド名につけることで、class内でしかアクセスできなくなる。
メソッドも同様にprivate修飾子をつけることで外側で使用できなくなる。
```ts
class Person {
  name: string;
  private age: number; // → private修飾子追加
  constructor(initName: string, initAge: number) {
    this.name = initName;
    this.age = initAge;
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
  }
}
const quill = new Person("Quill", 38);
quill.age = 50; // → エラー表示される。
```

### 初期化処理の省略

constructor()のパラメーターで、`修飾子 フィールド名: 型`と記述することで
classのフィールド名や`constructor()`の処理を省略できる。
```ts
// 省略していない場合
class Person {
  name: string;
  constructor(name: string) {
     this.name = initName;
  }
}

// 省略した場合
class Person {
  constructor(public name: string) {
  }
}

```

### readonly修飾子で書き換え不可にする

public修飾子、private修飾子の他にreadonly修飾子というものがある。
読み込み専門になる修飾子。
下記の例では`private age`に`readonly`修飾子も追加。
メソッドで書き換えようとするがエラー表示。
`readonly`修飾子を外せば正常に動作する。

```ts
class Person {
  constructor(public name: string, private readonly age: number) {}
  // メソッド追加
  incrementAge() {
    this.age = +1; // ageはreadonly修飾子がついているので、エラー
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
  }
}
const quill = new Person("Quill", 38);
quill.greeting();
```

![スクリーンショット 2022-11-25 18.17.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/27d5750f-a22c-3d8c-ae85-154636e79194.png)

### extendsを使用して他のclassの機能を継承

`class 新しいcalss名 extends 継承元class名 {}`
とすることで、継承元のcalssの機能を継承している新しいclassを生成できる。
```ts
class Person {
  constructor(public name: string, private age: number) {}
  incrementAge() {
    this.age = +1;
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
  }
}

class Teacher extends Person {} // extendsで継承したTeacher class

const quill = new Person("Quill", 38);
quill.greeting(); // → Hello! My name is Quill. I am 38 years old.
const tom = new Teacher("Tom", 28);
tom.greeting(); // → Hello! My name is Tom. I am 28 years old.
```

**constructor関数を拡張したい場合**


派生クラスのconstructor関数拡張には`super関数`の呼び出しを行う必要がある。
下記の例では派生クラスTeacherに新しいプロパティとして、subjectを追加している。

```ts
class Teacher extends Person {
  constructor(name: string, age: number, public subject: string) {
    super(name, age);
  }
}
```
継承元のメソッドも上書き、変更したい場合は単純に新しいclassで、そのメソッドと処理を記述する。
下記の例ではgreething()を上書き。
```ts
class Teacher extends Person {
  constructor(name: string, age: number, public subject: string) {
    super(name, age);
  }
  greeting() {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
  }
}
```
そのままだとエラーが発生！ageは継承元の`class Person`内で、`private修飾子`がついているため、そのclassの外ではアクセス不可。
![スクリーンショット 2022-11-25 18.46.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/75302d19-b518-3a9b-3464-803c1537e11d.png)
継承先では使いたいけど、publicで、どこからでもアクセスはしてほしくはない。
そんな場合に**protected修飾子**を使う。

### protected修飾子を使用し、継承先ではアクセスできるようにする

protected修飾子でクラスとその派生クラスではアクセスできるプロパティになる。

```ts
class Person {
    // ageの修飾子をprotectedに変更
  constructor(public name: string, protected age: number) {}
  incrementAge() {
    this.age = +1;
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
  }
}

class Teacher extends Person {
  constructor(name: string, age: number, public subject: string) {
    super(name, age);
  }
  greeting() {
    // 派生クラスでもthis.ageにアクセスできるようになる。
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
  }
}

const quill = new Person("Quill", 38);
quill.greeting();
const tom = new Teacher("Tom", 28, "Math");
tom.age = 30 // クラスや派生クラスの外ではアクセス不可でこちらはエラーになる。
tom.greeting();
```

### getterとsetterを作る

**getter**
何か取得した時に処理を発火したい場合に使用

**setter**
何か代入した時に処理を発火したい場合に使用

例)
subjectの値を取得(get)して空文字だったらエラー処理したい。
```ts
class Teacher extends Person {
  // get追加
  get subject() {
    if (!this._subject) {
      throw Error("There is no subject.");
    }
    return this._subject;
  }
  constructor(name: string, age: number, private _subject: string) { // private _subjectに変更
    super(name, age);
  }
  greeting() {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
  }
}

const tom = new Teacher("Tom", 28, "");
console.log(tom.subject); // → Error!空文字じゃなかったらthis._subjectの値が返ってくる。
```

例)
subjectの値を代入(set)して空文字だったらエラー処理したい。
```ts
class Teacher extends Person {
  get subject() {
    if (!this._subject) {
      throw Error("There is no subject.");
    }
    return this._subject;
  }
  // set追加。getterと同じ名前でも可。
  set subject(value) {
    if (!this._subject) {
      throw Error("There is no subject.");
    }
    this._subject = value;
  }
  constructor(name: string, age: number, private _subject: string) {
    super(name, age);
  }
  greeting() {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old. I teach ${this.subject}`);
  }
}

const tom = new Teacher("Tom", 28, "Math");
tom.greeting();
tom.subject = ""; // setterはこの代入した時に発火
console.log(tom.subject); // → 空文字なのでError!
```

### staticを使用してインスタンスを生成せず、Classを使用

`static`プロパティ/メソッドを使うと、newしてクラスのインスタンスを作らずとも、クラスのプロパティ、メソッドを使える。
```js
// staticメソッドの例 
Math.random()
```

例) 
Person Classにstaticプロパティ、メソッド追加。
`const quill = new Person("Quill", 38);`などのインスタンス生成は削除。
```ts
class Person {
  static species = "Homo sapiens"; // staticプロパティ追加
  static isAdult(age: number) { // staticメソッド追加
    if (age > 17) return true;
    return false;
  }
  constructor(public name: string, protected age: number) {}
  incrementAge() {
    this.age = +1;
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
  }
}

class Teacher extends Person {
// 省略
}

// インスタンスは生成していないのにstaticプロパティ、メソッドにはアクセス可能。
console.log(Person.species); // → Homo sapiens
console.log(Person.isAdult(14));  // → false

// 派生クラスも同様。
console.log(Teacher.species);  // → Homo sapiens
console.log(Teacher.isAdult(30));  // → true
```

`static`プロパティ/メソッドはインスタンスを生成しないためthisを使用できない。
thisはインスタンスを指すため。
thisは使用せず直接、`クラス名.プロパティ名`のようにアクセスする。
例)`Person.species`

### abstractクラスを使用して継承にのみ使用できるクラスを作成

abstract = 抽象的な

abstractが指定され、インスタンス化できない、継承元としてのみ使用するクラスを抽象クラスという。

> abstractは抽象クラスを作成する時に宣言します。抽象クラスとは直接インスタンス化(new)することができず、必ずスーパークラスとして利用することを保証するものです。

[サバイバルTypeScript:抽象クラス](https://typescriptbook.jp/reference/object-oriented/class/abstract-class)

クラスの前に`abstract修飾子`をつけることでそのクラスは抽象クラスだと明示でき、`abstractメソッド`や`abstractプロパティ`を使用できる。
つまりabstractクラスでしかabstractメソッド、プロパティは使用できない。

例)
Personクラスを抽象クラス化して、`greeting()`の中で派生クラスによって処理が異なるメソッドを実行する。
```ts
// Personクラスにabstract修飾子を追加して抽象クラス化
abstract class Person {
  static species = "Homo sapiens";
  static isAdult(age: number) {
    if (age > 17) return true;
    return false;
  }
  constructor(public name: string, protected age: number) {}
  incrementAge() {
    this.age = +1;
  }
  greeting(this: Person) {
    console.log(`Hello! My name is ${this.name}. I am ${this.age} years old.`);
    // 派生クラスによって処理が異なるメソッドexplainJobを実行。
    this.explainJob();
  }
  // abstractメソッドを定義。これによって派生クラスで、explainJob()があることを保証する。
  abstract explainJob(): void;
}

class Teacher extends Person {
// abstractメソッド、プロパティは必ず派生クラスで記述しなければならない。
  explainJob() {
    console.log(`I am a teacher and I teach ${this.subject}.`);
  }
  get subject() {
    if (!this._subject) {
      throw Error("There is no subject.");
    }
    return this._subject;
  }
  set subject(value) {
    if (!this._subject) {
      throw Error("There is no subject.");
    }
    this._subject = value;
  }
  constructor(name: string, age: number, private _subject: string) {
    super(name, age);
  }
}

const tom = new Teacher("Tom", 28, "Math");
tom.greeting(); // → Hello! My name is Tom. I am 28 years old.I am a teacher and I teach Math.
```

Personクラスをabstract化しないまま、クラス内でabstractを使用したり、Personクラス内のabstractメソッドに処理を書こうとしたり、派生クラスで、abstractメソッドを書き忘れたりするとエラーが出る。
![スクリーンショット 2022-11-26 10.16.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/3f5b10de-4a5d-d379-b94a-e4767a187474.png)
![スクリーンショット 2022-11-26 10.18.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/c43b7b97-743d-f143-ddc5-181ca734efc3.png)
![スクリーンショット 2022-11-26 10.19.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/e9932cb4-27f9-3b68-0ce3-e73afe929b09.png)

<br>

## interfaceの使い方

interfaceとはオブジェクトのみのtypeエイリアス。

> インターフェースはクラスが実装すべきフィールドやメソッドを定義した型

[サバイバルTypescript:インターフェース](https://typescriptbook.jp/reference/object-oriented/interface)

typeエイリアスとほとんど変わらないが、オブジェクトにのみinterfaceは使える。
interfaceがオブジェクトのみを扱うのでわかりやすいというメリットがあるらしい。
```ts
// typeエイリアス
type Human = {
  name: string;
  age: number;
};

const human: Human = {
  name: "Quill",
  age: 38,
};
let developer: Human;
```
```ts
// interface
interface Human {
  name: string;
  age: number;
};

const human: Human = {
  name: "Quill",
  age: 38,
};
let developer: Human;
```

### interfaceにメソッドを追加する方法

例1)
```ts
interface Human {
  name: string;
  age: number;
  greeting: (message: string) => void; // 追加
}

const human: Human = {
  name: "Quill",
  age: 38,
  greeting(message: string) { // 追加
    console.log(message);
  },
};
```
例2)
```ts
interface Human {
  name: string;
  age: number;
  greeting(message: string): void; // 変更
}

const human: Human = {
  name: "Quill",
  age: 38,
  greeting(message: string) {
    console.log(message);
  },
};
```

### implementsを使用して、クラスに対してinterfaceの条件を適応させる方法

`class クラス名 interface インターフェース名 {}`とクラスを作成することで指定したinterfaceの条件を満たすクラスを作成する

```ts
interface Human {
  name: string;
  age: number;
  greeting(message: string): void;
}

class Developer implements Human {
  constructor(public name: string, public age: number) {}
  greeting(message: string): void {
    console.log(message);
  }
}
```
interfaceを満たせていないとエラー表示
![スクリーンショット 2022-11-26 13.33.37.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2609332/e6f8f200-ccd8-35ad-451f-a77ef2fc1d32.png)

implementsは複数指定でき、そこに定義されているものは全て実装する
```ts
class Developer implements Human, test, sample { // 複数のimplements
  constructor(public name: string, public age: number) {}
  greeting(message: string): void {
    console.log(message);
  }
}
```

### 「?」を使ってあってもなくてもいいプロパティやパラメーターの作成

プロパティやパラメーター、メソッドの後に`?`をつけるとあってもなくても良いものになる。

```ts
interface Nameable {
  name: string;
  nickName?: string; // プロパティの後に?をつけるとあってもなくてもいいプロパティになる
}

const nameble: Nameable = {
  name: 'Quill'
}
```