🥃

【TS】型定義、完全に理解していた、、、😇

2024/10/13に公開

はじめに

どもども〜、てるし〜です。

みなさんはelmというものを知っていますか?
私はつい最近知りました。

友人との会話で、「てるし〜さん、elmやりましょうよ!」と言われたのでドキュメントを軽く読んでいました。
https://guide.elm-lang.jp/
プログラムの内容は慣れるまでには時間がかかりそうだが直感的に何をしているのかが分かり易いな〜という第一印象でした。
そのことを友人に伝えました。すると、彼の言葉からこのような用語が出てきました。

"Making Impossible States Impossible"

私は「???😇」となってしまいました。
ちなみにTSでもこの概念は使えるとのことで、、、
とりあえず訳がわからないのでGPT先生に意味を聞き、前回の記事にあった型定義を直してみました。

勉強し始めて2日目になるので間違っている部分も多々あると思うのでアドバイスいただけると嬉しいです。

前提

以前の記事を読んでおくと内容がわかりやすいかと思います。
https://zenn.dev/terusi/articles/79470ad2efe4c0

"Making Impossible States Impossible"とは

さぁ〜GPT様。今こそ活躍の時です!!!

「Making Impossible States Impossible」とは、無効な状態が発生しないように、型を使ってプログラムの矛盾を排除する設計手法です。これにより、実行時にバグやエラーが起きないようにすることができます。

要は存在しない状態が定義されないように型定義でガードするといった概念だと思われます。
TSは確かに「えいや〜〜」で動いてしまうことが多くそうするとバグが出てしまう可能性があるかなって思います。これを聞いた時、
「あ、やべ、型定義完全に理解しているかも、、、😇」
と思いました。何なら業務での型定義も、、、。

前回の型定義を見てみる

https://zenn.dev/terusi/articles/79470ad2efe4c0#1.-型定義

こちらの部分についてみてみます。
再度、定義の部分を見てみましょう。

import { Dispatch, ReactNode } from "react"

export type popupState = {
    isShow: boolean
    children?: ReactNode
}

export type popupAction = {
    type: "show" | "hide"
    children?: ReactNode
}

みなさんはこの型定義、安全だと思いますか?

この型定義はpopupが開いている状態とpopupの状態を変更するアクションの型を定義しています。childrenpopupのコンテンツの中身です。

ではいくつかの状態の例を見て考えてみましょう。

条件1. popupActionにてtypeshowchildrenundefined

この条件は求めている状態ではありません。
しかし実際のソースコード上でうっかりポップアップを開く処理を書くのにchildrenを忘れてしまったとしましょう。エディタ上で警告が出ることなくさらにビルドまで通ってしまいます。

条件2. popupActionにてtypehidechildrenに何か定義されている

上記同様、これも求めている条件ではないです。
条件1. よりは致命的ではないかもしれないですが例えばうっかりtypeshowにしてしまっていて
childrenundefinedとしている時、予期せぬバグが起こってしまうと思われます。

バグが起こると何がいけないの?

バグが起こると何がまずいのか?
まず、原因を探るところから始まるので無駄な工数が持ってかれます。
場合によっては1週間悩み続けることもあるかと思います。

その点エラーは何がいけないのかを言ってくれるのでありがたい限りです。

型定義修正

実際に型定義の部分を書き直してみたいと思います。

import { Dispatch, ReactNode } from "react"

type closeState = {
    isShow: false
}

type openState = {
    isShow: true
    children: ReactNode
}

export type popupState = closeState | openState

type closeAction = {
    type: "hide"
}

type openAction = {
    type: "show"
    children: ReactNode
}

export type popupAction = closeAction | openAction

ここでは閉じる時はchildrenundefined固定、開く時はchildrenを必須としています。
stateactionどちらも同様です。

こうすることで何が嬉しい??

では試しにactionの宣言部分を

dispatch({
    type: "show",
})

としてみましょう。
私はvscodeを使っているのですがその部分を見てみると、

ご覧の通りエラーが吐かれましたね。

さらにこれを放置しビルドすると、、、

失敗します。

エラーが出ることで事前にバグを防ぐことが可能になります!!!

上記コードは"Making Impossible States Impossible"に該当するのか?

試しにGPT様に聞いてみました。

はい、この定義は「Making Impossible States Impossible」の考え方に該当しています。具体的には、popupStateが「開いている状態」か「閉じている状態」のどちらかしか存在できないように型で表現しています。
なぜ該当するのか?
popupStateがcloseState(閉じている)かopenState(開いている)のどちらかしか取れないため、無効な状態(例: isShow: true なのに children がない)が発生しない。
同様に、popupActionもhide(閉じる)かshow(開く)のどちらかのアクションしか取れないので、アクションに応じた適切な状態遷移しか許容されません。
このように、無効な状態を型システムで防ぎ、コンパイル時にエラーとして検出できるようになっているため、この設計は「Making Impossible States Impossible」に該当しています。
よって、この実装は概念的に正しいといえます!

正しいとのことでした!やったぁ〜〜!!

最後に

いかがだったでしょうか。
TSは型定義次第で動いてしまい後々バグ報告をされてしまう可能性が十分にあると思いました。
"Making Impossible States Impossible"
という言葉を知れたことによってより厳密な型定義を勉強して今後の開発をより安全にできるように心がけていこうという考えに至ることができました。

elm自体がまだ始めたばかりで今後新たな発見がありそれがReactやTSに活かせることがたくさんあるかと思います。勉強がんばります!!!

ぜひ、みなさんにもこの言葉を知ってもらいたく記事にしました。

最後にとある人から贈られた言葉を皆さんにも送ります。

エラーは友達!!!!

それではまた〜👋

Discussion