React Router v7のtypegenを使い自動生成した型情報でパラメータに型を指定する
はじめに
2024/10/4にプレリリースされたReact Router v7
では、Typesafety
の改善としてreact-router typegen
というコマンドが追加されています。
このコマンドを実行する事で、各パスに紐づいているRoute
コンポーネントの型が自動生成されます。
この記事では、生成されたtypes
をアプリケーションで使用する為のセットアップと使い方を紹介します。
セットアップ
typegen
の実行
1. 早速、型ファイルを生成します。
bun react-router typegen
コマンドを実行すると型情報.react-router
ディレクトリに出力されます。
例えば、routes/users.$user/route.tsx
に対応するtypes
は以下の様になります。Params
型でパスパラメーターのid
が宣言されています。
// React Router generated types for route:
// routes/users.$user/route.tsx
import type * as T from "react-router/types";
export type Params = {
id: string;
};
type Route = typeof import("./route");
export type LoaderData = T.CreateLoaderData<Route>;
export type ActionData = T.CreateActionData<Route>;
export type LoaderArgs = T.CreateServerLoaderArgs<Params>;
export type ClientLoaderArgs = T.CreateClientLoaderArgs<Params, Route>;
export type ActionArgs = T.CreateServerActionArgs<Params>;
export type ClientActionArgs = T.CreateClientActionArgs<Params, Route>;
export type HydrateFallbackProps = T.CreateHydrateFallbackProps<Params>;
export type ComponentProps = T.CreateComponentProps<
Params,
LoaderData,
ActionData
>;
export type ErrorBoundaryProps = T.CreateErrorBoundaryProps<
Params,
LoaderData,
ActionData
>;
出力される型情報は以下の情報から型推論されています。
-
Params
: ファイルベースのルーティングを含むroutes.ts
からのパスパラメーター -
LoaderData
:Route
コンポーネント内のloader
,clientLoader
-
ActionData
:Route
コンポーネント内のaction
,clientAction
tsconfig,json
の更新
2. 生成された型ファイルにアクセスするためにtsconfig,json
を更新します。
.react-router/types
配下のパスはapp
配下のパスと同様になる為、Route
コンポーネントから./+types.route
の様にアクセスできる様になります。
{
+ "include": [".react-router/types/**/*"],
"compilerOptions": {
+ "rootDirs": [".", "./.react-router/types"]
}
}
また、plugin
を指定する事でroutes
が変更される度に型情報の自動生成が行えます。
{
"include": [".react-router/types/**/*"],
"compilerOptions": {
"rootDirs": [".", "./.react-router/types"]
+ "plugins": [{ "name": "@react-router/dev" }]
}
}
3. コンポーネントからアクセスする
以下の様にloader
でRoute
をimport
し、Route.LoaderArgs
の様に型情報にアクセスできます。
import type * as Route from "./+types.route";
export function loader({ params }: Route.LoaderArgs) {}
export default function Component({ loaderData }: Route.ComponentProps) {}
useParams
などloader
以外から参照する場合も同様です。
import type * as Route from "./+types.route";
export default function User() {
const params = useParams() as Route.Params;
const userId = params.id
}
変わったこと
useParams
の返り値の型はstring | undefined
なので、これまでは以下のいずれかでパラメーターをstring
として扱う必要がありました。
-
Params
型を明示的に定義する -
params.id!
としてundefined
の場合にエラーにする -
userId
が存在する事を保証する
import { useParams } from "react-router";
// 1. `Params`型を明示的に定義するパターン
type Params = {
id: string;
};
export default function User() {
const params = useParams() as Params;
// 2. `params.id!`として`undefined`の場合にエラーにするパターン
const userId = params.id!;
return (
// 3. `userId`が存在する事を保証するパターン
{userId && <UserEdit userId={userId} />}
)
}
これからは、自動生成された型情報にアクセスすることで以下のように記述することができます。
import type * as Route from "./+types.route";
export default function User() {
const params = useParams() as Route.Params;
const userId = params.id
return (
<UserEdit userId={userId} />
)
}
今後の補足情報
将来的には以下の要素なども追加される様です。
meta
links
headers
shouldRevalidate
また、Link
コンポーネントでもtype safeなparams
指定が行える様に計画しているとの事です。
終わりに
Remix v2
からの移行でReact Router v7
を使っているのですが、こうしたTypesafety
の改善は便利でワクワクしますね。今後のLink
コンポーネントの対応など、さらなる開発体験の向上が楽しみです。
環境
$ bun envinfo --npmPackages @react-router/node,@react-router/fs-routes,react-router,react-router-dom,@react-router/dev
npmPackages:
@react-router/dev: ^7.0.0-pre.1 => 7.0.0-pre.1
@react-router/fs-routes: ^7.0.0-pre.1 => 7.0.0-pre.1
@react-router/node: ^7.0.0-pre.1 => 7.0.0-pre.1
react-router: ^7.0.0-pre.1 => 7.0.0-pre.1
react-router-dom: ^7.0.0-pre.1 => 7.0.0-pre.1
Discussion