Typescriptの型定義

# はじめに
Typescriptの学習を始めた初学者です。
慣れない型定義の記述を備忘録としてまとめています。
記述に誤りがある場合など、ご指摘いただけると幸いです！


# 型アノテーションの基本の書き方
```ts:
// 変数
let height: number = 170;

// 定数
const weight: number = 60;

// 関数
const getTriangle(base: number, height: number) : number {
    return base * height / 2;
}

// クラス
class Person {
    name: string = "山田太郎";
    age: number = 20;

    introduce(): string {
        return `私の名前は${this.name}です。年齢は${this.number}です。`
    }
}
```

# TypeScriptのデータ型
## プリミティブ型
```ts:
// 数値型
const age: number = 30;

// 真偽型
const isMarried: boolean = true;

// 文字列型
const name: string = "John";

// symbol型(一意の値)
const uniqueString: symbol = Symbol("unique");

// undifined型(値が定義されていない状態)
const notDefined: undefined = undefined;

// null型(値が存在しない状態)
const empty: null = null;

// bigint型
const number: bigint = 12345678901234567890n; 
```

## 配列型
```ts:
const language: string[] = ['JavaScript', 'Ruby', 'Python'];
```

## タプル型
```ts
const person: [srting, number, boolean] = ['佐藤三郎', 50, false];
```

## オブジェクト型
```ts
let member: {
    name: string,
    age: number,
    married: boolean
} = {
    name: '山田太郎',
    age: 50,
    married: true
};
```

<details><summary>undefinedとnullの違い</summary>

#### undefined
代入されていないために、値が存在しない場合は`undefined`
初期化されていない変数定義などに自動で定義がされる。

```ts:例
let value;
console.log(value);　// => undefined(letで定義されたのみのため)

const obj = {}
console.log(obj.foo) // => undefined(値が存在しないため)
```

#### null
代入すべき値が存在しない(空)ため、値がない。
意図的に値が存在しないことを表現する場合に使用する。

```ts:例
const name: string | null = null;
```
</details>

## Typescript固有の型
### any型
どんな値でも代入できる型。型が不明な場合に使用できる。
便利だがどのような値でも許容してしまうためTypescriptの良さが薄れるので、極力使用を避ける。
```ts:
let age: any;

age = 30;     // 数値が入ってもOK
age = '30歳'  // 文字列が入ってもOK
```

### unknown型
どんな値でも代入できる型。型が不明な場合に使用できるが、値に対する制限がある。(その値を用いた演算ができないなど)

`any型`よりも安全性の高い型と言える。
```ts
// unknown型なのでどんな値でも代入できる。
const x: unknown = 100;

// 数値型でないので(unknown型なので)、乗算ができない
console.log(x * 3); => 'x' is of type 'unknown'エラーが発生。
```

### void型
値が存在しないことを示す型。
関数が何も返さない(戻り値がない)場合に使用する。
```ts:
const greet = (name: string): void => {
    console.log(`私の名前は${name}です。`);
}
```

### never型
何も戻り値として返さないことを示す型。
エラーを投げる関数や無限ループ関数などの戻り値として使用する。

※voidとの違い：関数の終端に到達できない場合には`never`、関数の実行完了までする場合は`void`を使用。
```ts
const throwError = (): never => {
    throw new Error('Error');
}
```

# 複数の型を組み合わせる場合

## 共有体型
複数の型のいずれかを指定する場合に使用する。
```ts
// 数値でも文字列でも代入できる。
let age: string | number;
age = 30;
age = '30歳';

// 配列の場合
let person: (string|number)[] = ['山田太郎', 40, '男性'];
```

## 型エイリアス
既存の方を別の名前で定義する機能のこと。
`type 型名 = 型定義`の構文で定義することで、その他の型のように利用ができる。

```ts
type Person = {
    name: string,
    age: number,
    gender: string
}

let employee: Person = {
    name: '山田太郎',
    age: 35,
    gender: '男性'
}
```

## 構造的部分型
構造的部分型とは、「型の名前ではなく型の中身(構造)」を見て互換性を判断する判定方法。

```ts:例
type Summary = { name: string }; 
type Detail = { name: string; age: number };

// Summary型に対してDetail型のオブジェクトは代入できる。
// 理由はSummary型はnameプロパティのみ要求しているので、Detail型のオブジェクトは必要な要件を満たせているため。
// TypeScriptではより多くのプロパティを持つ型を、より少ないプロパティを要求する型に代入できる。

const TaroDetail: Detail = { name: "Taro", age: 30 };
const summary: Summary = TaroDetail;  

// Detail型に必要なageプロパティが不足しているのでNG
const TaroSummary: Summary = { name: "Taro" };
const detail: Detail = TaroSummary; // =>エラー

```

## ジェネリクス
クラスやメソッドに対して特定の型を紐づけ、引数のように扱える仕組み。

ジェネリクスは具体的な型を指定するのではなく、型の「テンプレート」を指定できる。つまりどんな型でも代入できるため、重複したコードを避けることができる。

### 例）ジェネリクスを使用しない場合
引数から値を受け取り、関数の中で処理を実行するという流れが共通している関数がある場合、型が異なるので2回同じような記述をしなければならない。
```ts:例
// 引数で数値を受け取り2倍にする関数
const returnNumber = (n: number): number  =>  {
    return n;  
}

// 引数で文字列を受け取って大文字に変換する変数
const returnString = (s: string): string => {
    return s;
}

console.log(doubleNumber(2));
console.log(capitalizeString('i'));
```

冗長的な記述を書き換えるために、ジェネリクスを用いて型を設定する。

例）ジェネリクスを使用した場合
```ts
const checkArg = <T>(arg: T): T => {
    return arg;
};

console.log(checkArg<number>(2));         // 数値を指定すると、2が返ってくる
console.log(checkArg<string>('test'));     // 文字列を指定すると、testが返ってくる

```