【TypeScript】any を厳重に扱う

## はじめに
この記事では、`TypeScript`における `any型` をなぜ厳重に扱う必要があるのかについて、まとめています。


## まずは TypeScript と 静的型付け について
TypeScriptは、JavaScript のスーパーセットとして 静的型付け を提供するプログラミング言語です。

静的型付け とは、変数や関数などの値 に対して、コンパイル時または実行前に 型 を指定することです。この静的型付けによって、開発者はコードの品質を向上させ、バグを予防することができます。また、IDEの補完機能 や ドキュメント生成 などの恩恵も受けることができます。


## any型 を なぜ使ってはいけないか
any型 は `型を明示的に宣言しない場合` に割り当てられるデフォルトの型です。
any型 を使用すると、次のような問題が発生します。

### 1. **型安全性 の低下**
any型 を使用すると、型の厳密なチェック を 無効にします。
これにより、コンパイル時に型の不整合を検出できなくなり、ランタイムエラーのリスク が高まってしまいます。

### 2. **ドキュメント の不足**
any型 を使用すると、関数 や メソッドの引数 や 戻り値 の型情報 が失われます。
これにより、コードの読みやすさ や メンテナンス性 が低下してしまいます。

### 3. **チーム開発 の障害**
any型 を使用すると、コードの品質が一貫しなくなります。
これにより、チームメンバー間での コードの理解や協力 が困難になってしまいます。

### 要するに
any型 を使用すると、型の恩恵を受けられなくなり、TypeScriptの静的型付けの利点である コードの安全性 や 保守性 を失うことになってしまいます。

そのため、any型 の使用はできるだけ避け、厳重に扱うべきです。

```tsx
let data: any;

// 以下は、すべてコンパイルエラーにならない。。
data = 111;
data = "string";
data = { name: "あああ" }; 
```

### Strictモードを有効にする
TypeScriptのStrictモードを有効にすると、any型 の使用が制限され、型安全性が向上します。以下のように、コンパイラオプション strict を true に設定することで 有効化 できます。
```tsx
// tsconfig.json
{
    "compilerOptions": {
        "strict": true
    }
}
```


## それでも any型 を使う場面
一方で、any型 を使用すべき場面もあります。
以下は、any型 を使用する妥当なケースです。

### 1. **既存のJavaScriptコード の移行**
TypeScriptプロジェクト で JavaScriptコード を使用している場合、一度に全ての型を明示的に定義することは現実的ではありません。
このような場合、any型 を一時的に使用して コードを移行することができます。

### 2. **外部ライブラリとの統合**
外部ライブラリ や API が 型定義ファイル を提供していない場合、any型 を使用して そのライブラリと統合することができます。
ただし、可能な限り、型定義ファイルを作成するか、型安全な方法 で ライブラリを使用することが望ましいです。


## as演算子（型アサーション） を代わりに使うのはどうか？
any型 を避けるための方法の1つに、as演算子(型アサーション)の使用 があります。
型アサーションは、コンパイラに対して「私はこの値が指定した型であることを保証します」と伝える手段です。
as を使用すると、変数や式に `明示的な型を付与`することができます。
```typescript
let userInput: any; // any の使用
let userName: string;

userInput = 5; // any なのでなんでもアリ
// userInput は string ではなく any なので エラーが出る。
userName = userInput as string; // No error, but potential runtime error if userInput is not a string

userInput = 'Max'; // any なのでなんでもアリ
// 明示的に string を指定すると エラーが出なくなる。
userName = userInput as string; // No error
```


ただしこれは、型の不整合 を回避するための手段 であり、`型安全性を保証するものではありません`。そのため、as演算子(型アサーション) もまた、使用を避けるべきです。


## any も as も最終手段。 unknown型 を積極的に使おう
TypeScriptでは、any型 や as演算子 を使用して型解決を回避できますが、最適な回避策としては、`unknown型`の使用 が推奨されます。

### unknown型とは
TypeScript 3.0 から導入された型であり、`不特定の値` を表します。

### unknown型のメリット
#### 1. 型安全性の維持
unknown型 を使用すると、型情報を失うことなく型安全性を維持できます。
コンパイラ は unknown型 の値に対しては 型チェック を強制し、適切な型の操作を行うことを要求します。

#### 2. 型の明示的な宣言
unknown型 を使用すると、明示的に「これは未知の型です」と宣言できます。
これにより、コードの他の部分で 型が確定するまで、安全に操作を行うことができます。

#### 3. 安全な型の変換
unknown型 を使用すると、unknown型 から 別の型 に変換する場合、型ガード を使用して安全に変換できます。
これにより、ランタイムエラー を防ぎながら 型の安全な変換 が可能となります。

### unknown型の注意点
unknown型 は安全で柔軟な型であり、any型 や as演算子 よりも 型安全性を維持するための効果的な方法です。ただし、unknown型 からの 型の変換や操作 を行う際には注意が必要です。
明示的な型の確認 や 型の変換 が必要な場合は、型ガード を使用して安全に行うようにしましょう。これにより、コードの安全性と保守性を高めることができます。


```typescript
let userInput: unknown; // unknown型 を使用
let userName: string;

userInput = 5;
userName = userInput; // Error: Type 'unknown' is not assignable to type 'string'.

// userInput は string型 として安全に扱える
if (typeof userInput === 'string') { // 型ガード で、userInput が string型 かどうかを確認する
    userName = userInput; // No error
}
```


## まとめ
いかがだったでしょうか？

TypeScriptでは、`型安全性`が重要です。
any型 を使用すると、型の恩恵を受けづらくなってしまうため、できる限り型を明示的に定義し、`any型 の使用を避ける`よう努めるべきです。

any型 の代わりに、as演算子（型アサーション） を使用する方法もありますが、その場合は unknown型 を積極的に使用しましょう。

以上、適切な型を使用し、コードの品質とメンテナンス性を高めていきましょう。


## 参照
https://zenn.dev/y_yuita/articles/2e59b23169307c

https://typescriptbook.jp/

https://typescriptbook.jp/overview/static-type