フロントエンド学習記No.2 TypeScriptで静的型付けの安全性を知る

## はじめに
Progaku Advent Calendar 2023
25日目の投稿になります。

フロントエンド学習記No.2です！
今回は前回JavaScriptで作成したアプリをTypeScriptで書き換えながら学んでいきます。

静的型付けによる安全性、バグ検知の利点を初学者なりに少しでも実感することが目標です！

https://typescriptbook.jp/overview/features


前回の記事はこちら

https://qiita.com/rikuya98/items/2387352f8d4daeee5c62

## 環境構築

https://qiita.com/ochiochi/items/efdaa0ae7d8c972c8103

こちらの記事を参考に行いました。
tsc = TypeScriptのコンパイラをターミナル上で実行し、jsファイルを生成して動かしていきます。

## TypeSriptで書いてみる
とりあえず、前回のソースをそのままtsファイルに貼り付け
![スクリーンショット 2023-12-23 16.27.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3301763/c1f92184-31ce-a7a6-b07f-562bd6118d8c.png)

早速、型チェックによるたくさんのエラー、警告が出ていますね。
一つずつ見ていきます。


### 非アサーション演算子でnullでないことを保証する
```TypeScript
function updateMeter(counterValue) {
    const meter = document.querySelector('#meter');
    const maxWidth = 200;
    const newWidth = (maxWidth / 10) * counterValue;
    meter.setAttribute('width', newWidth);
}
```

meterはnullの可能性がありますよ、と言っています。
これはquerySelectorはelement型とnullを返すためのようです。
その上、meterが存在しない場合の処理も書いていないので怒られているわけですね。

そういった時に値がnullでないことを証明する非アサーション演算子というものがTypeScriptにはあるようです。
以下参考記事

https://qiita.com/k_kind/items/0e24c54da6fb44403e97

今回で言うとsvg要素であるmeterは確実に存在するので使っていきます。

```TypeScript
const meter = document.querySelector('#meter')! as SVGElement;
```
末尾に!をつけるだけで使用できます。
そして型推論でElement型と認識してくれていましたが、せっかくなので明示的に型も定義していきます
SVGの要素はSVGElement型のようです。

CountUpButton
CountDownButton
こちらも同様にnulをとらず確実に存在することを示すため非アサーション演算子を適用していきます。
```TypeScript
const countUpButton = document.querySelector('#countup')! as HTMLButtonElement;
const countDownButton = document.querySelector('#countdown')! as HTMLButtonElement;
```


### HTMLの属性の値は文字列型と知る
次が、個人的に驚いたのですが（無知なだけですが笑）

```
    meter.setAttribute('width', newWidth);
```
ここですね。
meterのwidth属性に現在のカウントに対応するwidthを反映する処理なのですがnumber型であるnewWidthをstringのパラメーターに入れることはできないと言っています。
調べるとHTMLの属性は、文字列型なんですね。
widthなどは数値型と勝手に思い込んでいました。
なのでsetAttributeは第１引数に属性名、第２引数に文字列型をとるわけですね。
そして普通にmdnにも書いていましたね・・・
ちゃんと読んでいなくてごめんなさい・・・

https://developer.mozilla.org/ja/docs/Web/API/Element/setAttribute

文字列以外をとった時は自動的に変換してくれていたようです。

暗黙的に文字列に変換してくれていたのは他にあるわけでして

レベルの値の加算箇所
```typescript
    levelElement.textContent = levelValue + 1;
```
メータの値をセットする箇所
```typescript
    counterElement.textContent = currentValue;
```

文字列を取るtextContentにしっかりと数値型を入れてしまっていましたね・・


テンプレートリテラルで文字列にして修正します。

```typescript
levelElement.textContent = `${levelValue + 1}`;
```

```typescript
counterElement.textContent = `${currentValue}`;

```

最後に、updateConterメソッド内ですね。
counter要素とLevelCountの要素があることを保証しても、その中のtextContetがnullの可能性もあリます。
それを伝えてくれていました。

```typescript
function updateCounter(amount: number) {
    if (!counterElement.textContent || !levelElement.textContent){
        console.error('必要な要素が存在しません。')
        return
    }
```
!　＝　not演算子です。
値が存在しない場合にerrorを表示して処理を止めるように追加。


そうして
TypeSriptのコンパイラのご指導のもと修正したコードがこちらになります。

```typescript
const INCREMENT: number = 1;
const DECREMENT: number = -1

function updateMeter(counterValue: number) {
    const meter = document.querySelector('#meter')! as SVGElement;
    const maxWidth = 200;
    const newWidth: number = (maxWidth / 10) * counterValue;
    meter.setAttribute('width', `${newWidth}`);
}

const counterElement = document.querySelector('#counter')! as HTMLParagraphElement ;
const levelElement = document.querySelector('#LevelCount')! as HTMLParagraphElement;

function updateCounter(amount: number) {
    if (!counterElement.textContent || !levelElement.textContent){
        console.error('必要な要素が存在しません。')
        return
    }
    let currentValue = parseInt(counterElement.textContent);
    let levelValue = parseInt(levelElement.textContent);

    if (amount > 0 && currentValue < 10) {
        currentValue += amount;
    } else if (amount < 0 && currentValue > 0) {
        currentValue += amount;
    } else if (currentValue === 10) {
        levelElement.textContent = `${levelValue + 1}`;
        currentValue = 0;
        window.alert('おめでとうございます！レベルアップしました！');
    }

    counterElement.textContent = `${currentValue}`;
    updateMeter(currentValue)
}

const countUpButton = document.querySelector('#countup')! as HTMLButtonElement;
countUpButton.addEventListener('click', () => updateCounter(INCREMENT));

const countDownButton = document.querySelector('#countdown')! as HTMLButtonElement;
countDownButton.addEventListener('click', () => updateCounter(DECREMENT));


```

## まとめ
 TypeSriptを使ってみて、コンパイラに怒られまくることで、自身のコードの安全性のなさ、コードを書く力の低さを改めて実感しました・・・笑
 ただ、コードを実行する前に、型チェック等で知らせてくれることで、そうした不備に気づくことができるというのがTypeSriptや静的型付け言語の大きなメリットの一つだと実感しました。
 その特性を活かして、適切に型を定義したり、コードを書くことができていれば修正等で誤った値が入った際などにきちんとエラーが吐き出されたり、原因を特定しやすくなるのかなと。大規模なシステムで多数のエンジニアで開発する際などよりメリットが大きくなりそうですね。

 ## その他参考記事

https://qiita.com/masayasviel/items/d30d19af7ba77ed9a316

 https://qiita.com/masayasviel/items/577020381900c529a1cc