TypeScriptの名前空間を活用した
UIコンポーネントの設計と
型安全性の追求

TSKaigi Kansai@みやこめっせ京都
saku🌸 / @sakupi01
Page 1 of 28

みなさんは、tsxでUIコンポーネントを作成する際に、どのように実装していますか?

私も含め、ほとんどの人が以下のようにButtonコンポーネントとそれに関連するButtonPropsというふうに定義していると思います

そして、その拡張であるNavigationButtonコンポーネントとそれに関連する型となると以下のように定義していると思います。

しかしこのEnhancedNavigationButtonのように、拡張を重ねていくとProps名がやたら冗長になることがあります

このようにコンポーネント名を冠したPropsの命名には限界があります

コンパニオンオブジェクトパターンを知る前に、 TypeScriptは値と型を別物として扱うということを再確認したいです

コンパニオンオブジェクトパターンでは、値と型が別物ということを生かし、値と型を同名で定義することでセットで扱うことができるというものです

このように、コンポーネントとPropsをどちらもButtonとして定義することで、セットで管理することができる。 このように、Buttonとするだけで型の意味でも値の意味でもButtonとして扱うことができる

まず、本題に入る前に、TypeScriptの値と型について定義したいと思います

これらを踏まえて、`NavigationButton.Props`を表現してみます

まず、ESMの`import as`・`export as`を使うと、このようにimportのタイミングで名前空間オブジェクトを生成して、型をモジュール化することができます。

しかし、ESMの`import as`・`export as`を使うと微妙な点がいくつかあります

そこで、namespaceを使って型をモジュール化してみると、以下のようにnavigation-button.tsxで命名せずにすみ、かつ、re-exportのためのファイルを経由することなくNavigationButton.Propsと表現することができます。

--- ### TSの`namespace`を用いてデータ型の構造をモジュールで表現する - 利用側は定義された名前空間オブジェクト名をそのまま使用できる - **命名の一貫性を保つことができる** - 同一ファイル内で型をモジュール化 - **re-exportのためのファイルが不要**

さらに、UIコンポーネントの設計においてTypeScriptのnamespaceを活用することで、コンポーネントに関連する型定義を一箇所にまとめることができたりもします