[TypeScript] 配列をマスターしよう！

### はじめに
早くもTypeScript4件目となる記事です...
今回は配列についてまとめてみました。

### 【目次】
[まずは基本的な型宣言をマスターしよう！](https://qiita.com/PenPe/items/c189a611b3407b7ceef9)
[オブジェクト型の型宣言をマスターしよう！](https://qiita.com/PenPe/items/440683cc6e5e4ac0ca32)
[関数の型定義をマスターしよう！](https://qiita.com/PenPe/items/bff109c6f83f46d191a7)
[配列をマスターしよう！](https://qiita.com/PenPe/items/7e056eec2cce76a745a9) ←🈁
[インターフェースをマスターしよう！](https://qiita.com/PenPe/items/f9314aeccb2f23cac382)
[クラスを使いこなそう！](https://qiita.com/PenPe/items/c8e6289f386c4db23a6f)
[型修飾子についての理解を深めよう！](https://qiita.com/PenPe/items/7af57861e93830fe9a79)
[ジェネリックをマスターしよう！](https://qiita.com/PenPe/items/fde6a342e746e265d907)
[独自の構文拡張をマスターしよう！](https://qiita.com/PenPe/items/c79cdb5bc4a68a1dfd7f)

---

### TypeScriptにおける配列
TypeScriptにおける配列のベストプラクティスとして、
**「1つの配列につき、1つのデータ型」** という思考を尊重しています。

そのため既存の配列と異なる型のデータを挿入しようとすると **型エラー** が発生します。

```typescript
let strs = ["Hello", "World"]

// OK
strs.push("!")

// NG: Argument of type 'number' is not assignable to parameter of type 'string'.
strs.push(1234)
```

---

### 配列の型定義
配列の型定義は以下の形式で行います。
`let 変数名: データ型[]`
```typescript
let strs: string[] = [];
let nums: number[] = [];
```

---

### 合併型の配列
**「1つの配列につき、1つのデータ型」** というベストプラクティスは理解できましたが、異なるデータ型を1つの配列で扱いたいこともありますよね。

そのようなケースでは、合併型の配列を利用します。
`let 変数名: (型A | 型B)[]`

```typescript
// string or number型のデータを格納する配列
let stringAndNumbers: (string | number)[] = [];

// OK
stringAndNumbers.push(1234);
stringAndNumbers.push("1234");

// NG: Argument of type 'boolean' is not assignable to parameter of type 'string | number'.
stringAndNumbers.push(true);
```

#### :memo: any型について
異なるデータ型を格納する配列の定義として`any[]`を利用することもできます。
しかし、`any`型は間違った値の追加を許してしまう可能性があり、TypeScriptの恩恵を十分に受け取れません。

よほどのことがない限り利用しないようにしましょう。
```typescript
let anyArray: any[] = []
```

---

### タプル
タプルとは、固定サイズの配列です。
それぞれのインデックスにおいて、決まった型を持ちます。

以下の例において、TypeScritは以下の型定義を推論します。
- `tuple[0]` は `number`型
- `tuple[1]` は `string`型

そのため、各データ型の固有メソッドが型の絞り込みなしに実行できます。
```typescript
let tuple: [number, string]
tuple = [1234, "Hello"]

// OK
tuple[0].toFixed
// OK
tuple[1].toUpperCase
```

---

#### タプルへの値割当
タプル型は可変長引数よりも限定的な型です。
タプル型の変数に対して、可変長引数の割り当てはできません。

変数`list`はTypeScriptによって`(string | number)[]`型と型推論されています。
そのため、タプル型の`[string, number]`に割り当てしようとするとエラーが発生します。

```typescript
let list = ["str", 1234];

// NG: Type '(string | number)[]' is not assignable to type '[string, number]'
let tuple: [string, number] = list

// OK
let listSecond: [string, number] = ["str", 1234];
```

#### スプレッド引数とタプル
TypeScriptはスプレッド変数に対しても、正確な型チェックを行います。

変数`args` は `(string | number)[]` 型と型推論されています。

関数`fn`にスプレッド引数として渡されていますが、渡された引数はそれぞれ `string | number` と推論されるため、型エラーが発生します。

```typescript
function fn(input: string, num: number) {
    console.log(input, num)
}

let args = ["inputText", 1234]

// NG: A spread argument must either have a tuple type or be passed to a rest parameter.
fn(...args);
```

これを改善するには、配列をタプル型として定義します。
こうすることで引数1が`string`、 引数2が`number`型と推論され、型エラーが解消されます。
```typescript
function fn(input: string, num: number) {
    console.log(input, num)
}

let args:[string, number] = ["inputText", 1234]

// OK
fn(...args);
```

#### タプルの型推論
前述したように、TypeScriptはコード内で生成された配列を可変長引数としてみなします。

上記のようなケースでは、配列がタプル型であることを宣言する必要があります。
配列がタプル型であることを示すためのアプローチを**2つ**ご紹介します。

##### 明示的なタプル型として定義
以下の関数`fn`は戻り値としてタプル型を定義しています。
この変数の戻り値を、TypeScriptは`[string, number]`のタプルであることを推論します。

よって、`outputPair(...returnPair())` は型エラーが発生しません。
```typescript
function returnPair(): [string, number] {
    return ["CodeTaro", 24]
}

function outputPair(name: string, age: number) {
    console.log(name, age)
}

// OK
outputPair(...returnPair());
```

##### constアサーション
値の末尾に `as const`アサーションを定義することで、値が読み取り専用であることを推論します。

`as const`アサーションを配列の末尾に付与することで、TypeScript はその配列をタプル型として扱うようになります。

```typescript
function outputPair(name: string, age: number) {
    console.log(name, age)
}

let pair = ["Taro", 24] as const;
// OK
outputPair(...pair);
```

:memo: `const`アサーションを付与した変数に対して、値の更新を実行すると型エラーが発生しm
```typescript
let pair = ["Taro", 24] as const;

// NG: Cannot assign to '0' because it is a read-only property.
pair[0] = "Tom"
```

---

### まとめ
以上です！
思った以上に濃い内容となりました。
型推論は素晴らしい... :sparkles:

次節として、**インターフェース** に関する記事を公開しておりますので、よろしければご覧ください🙏

https://qiita.com/PenPe/items/f9314aeccb2f23cac382