【ITニュース解説】Type-safe routing with less code in React
2025年09月12日に「Dev.to」が公開したITニュース「Type-safe routing with less code in React」について初心者にもわかりやすく解説しています。
ITニュース概要
Reactのルーティングで、URLのパスから取得するデータ(パラメーター)に、より厳密な「型」を設定し、型安全なコードを書く方法を紹介。`url-shape`や`zod`を使ってURLスキーマを定義することで、少ないコードでエラーを防ぎ、堅牢なアプリケーション開発に貢献する。初心者でも段階的に導入できる。
ITニュース解説
Webアプリケーションを開発する際、ユーザーがアクセスするURL(ウェブサイトのアドレス)に応じて、表示する内容を切り替える機能が不可欠です。これを「ルーティング」と呼びます。例えば、ウェブサイトのトップページは「/」、商品の詳細ページは「/products/123」のように、URLが変わると画面の内容も変わります。ReactのようなJavaScriptのライブラリでアプリケーションを作る場合、このルーティングの仕組みをどのように効率的かつ安全に実装するかは、開発者にとって重要な課題の一つです。
この記事では、Reactアプリケーションにおいて、コード量を少なく保ちながら、より「型安全」なルーティングを実現する新しいアプローチが紹介されています。型安全とは、プログラムが扱うデータの「型」が、想定しているものと異なる場合にエラーを発生させることで、意図しないバグを防ぐ仕組みのことです。例えば、数値として扱いたいデータが誤って文字列として扱われると、計算が正しく行えないなどの問題が発生する可能性があります。
従来のルーティングの仕組みでは、URLに含まれる動的な部分、例えば「/sections/123」というURLの「123」のような部分(これを「パラメーター」と呼びます)は、多くの場合、自動的に「文字列」として扱われます。たとえ、開発者としてはこの「123」が「セクションのID」という「数値」だとわかっていても、プログラム上では単なる文字列として認識されるのです。その結果、この「123」を数値として計算に使いたい場合は、parseInt()のような関数を使って手動で文字列から数値に変換する手間が必要になります。そして、もし「/sections/abc」のような数値ではない文字列が来たら、変換エラーや予期せぬ挙動につながる可能性もあります。このように、パラメーターの型が常に Record<string, string | undefined>(キーが文字列で、値が文字列かundefined)のように曖昧だと、開発中に不注意によるバグが生まれやすくなります。
この問題に対し、紹介されている方法は url-shape と zod という二つのライブラリを組み合わせることで、URLの構造(スキーマ)とそこに渡されるパラメーターの型を事前に厳密に定義し、自動的に型安全なルーティングを可能にするものです。
まず、createURLSchemaという関数を使って、アプリケーションで使われるURLのパスと、それぞれのパスが受け取るパラメーターの型を定義します。例えば、トップページを表す「/」パスはパラメーターを受け取らないため null と定義します。一方、「/sections/:id」というパスでは、:id の部分がパラメーターになります。ここでは、zod ライブラリの z.object を使って、この id が数値型であることを z.coerce.number() で明示的に指定します。z.coerce.number() は、文字列を自動的に数値に変換してくれる便利な機能も持っています。
このようにURLスキーマを一度定義すると、createURLSchema から url という関数が生成されます。この url 関数を使うことで、文字列のパスを直接書く代わりに、定義されたスキーマに沿った型安全なURLパスを生成できるようになります。例えば、トップページへのリンクを作る場合は url('/') と書き、セクション1へのリンクを作る場合は url('/sections/:id', {params: {id: 1}}) のように書きます。ここで注目すべきは、id: 1 と数値を渡している点です。これにより、url 関数は定義されたスキーマに従って、id パラメーターが数値であることを認識し、後続の処理でも数値として扱われるようになります。
アプリケーションのコンポーネント内では、useRoute() フックから取得できる withRoute という関数がルーティングの核となります。この withRoute(routePattern, x, y) 関数は、JavaScriptの三項演算子(条件 ? 値1 : 値2)に似た働きをします。現在のURLが routePattern(URLのパターン)に一致すれば x を返し、一致しなければ y を返します。x や y には、表示したいReactコンポーネントや、HTML要素のクラス名などのプロパティの値を指定できます。
この withRoute 関数と、url() 関数で生成した型安全なパスを組み合わせることで、非常にすっきりとしたコードで、かつ安全にルーティングを記述できます。例えば、
1<nav className={withRoute(url('/'), 'full', 'compact')}> 2 {/* … ナビゲーションリンク … */} 3</nav> 4{withRoute(url('/'), <Intro/>)} 5{withRoute(url('/sections/:id'), ({params}) => ( 6 <Section id={params.id}/> 7))}
といったコードになります。ここで、withRoute(url('/sections/:id'), ({params}) => (<Section id={params.id}/>)) の部分では、現在のURLが /sections/:id のパターンに一致した場合、Section コンポーネントを表示しています。このとき、params オブジェクトの id プロパティは、事前に定義したスキーマのおかげで、もはや単なる文字列ではなく「数値」として扱われます。これにより、開発者は params.id を安心して数値として利用でき、手動での型変換やエラーチェックのコードを減らすことができます。これが「型安全なルーティング」の大きなメリットです。
このアプローチの利点は多岐にわたります。まず、パラメーターの型が明確になることで、開発中のバグを大幅に減らせます。エディターの補完機能も正確に働き、開発効率が向上します。また、コードの意図が明確になるため、将来の機能追加や変更、あるいは他の開発者がコードを理解する際の負担が軽減され、保守性が向上します。
さらに、この方法は「段階的な導入」が可能です。アプリケーション全体を一度に型安全なルーティングに切り替える必要はなく、一部のルーティングから少しずつ導入していくことができます。これは、既存の大きなプロジェクトに新しい技術を取り入れる際に非常に役立ちます。
もしアプリケーション全体で徹底的に型安全なルーティングを強制したい場合は、オプションとしてルーターの設定を厳格(strict: true)にすることもできます。これにより、url() 関数で生成されたパス以外、つまり手書きの文字列や正規表現で記述されたURLパターンを全面的に禁止し、アプリケーション全体の型安全性をさらに高めることが可能です。ただし、これは完全にオプションであり、プロジェクトのニーズに合わせて選択できます。
また、URLスキーマは必ずしも一箇所にまとめて定義する必要はありません。アプリケーションの特定の機能や部分ごとに、複数の部分的なURLスキーマを作成し、それを組み合わせて使うこともできます。これにより、大規模なアプリケーションでも、URLスキーマの管理がしやすくなります。
このように、このアプローチは、少ないコード量で、Reactアプリケーションのルーティングに高い型安全性をもたらします。システムエンジニアとして、このような効率的で安全な開発手法を知り、使いこなすことは、質の高いソフトウェアを開発するために非常に重要です。