TypeScriptテンプレートリテラル型で作る型安全なURLルーター
TypeScript Routing Frontend
結論
Template Literal Types と infer キーワードを組み合わせ、再帰抽出型を作成します。
// TypeScript公式仕様:パラメータ抽出型の定義
type ExtractParams<Path extends string> =
Path extends `${string}:${infer Param}/${infer Rest}`
? Param | ExtractParams<`/${Rest}`>
: Path extends `${string}:${infer Param}`
? Param
: never;
// 抽出したキーをオブジェクト型へ変換
type PathParams<T extends string> = {
[K in ExtractParams<T>]: string;
};
TypeScript公式仕様:Template Literal Types の仕組み
TypeScript(4.1以上)で導入された Template Literal Types は、文字列型のリテラル("/users/:id")をパターンマッチングし、infer キーワードで任意のサブ文字列を取り出す機能です。
この機能を活用することで、react-router や Next.js のようなURLルーターに対して、型安全なパラメータ補完を提供できます。
実際に起こる事故:TS2589 コンパイルエラーの発生
複雑なネストや長すぎるURL文字列に対して型抽出を実行した際、TypeScriptコンパイラの再帰限界上限に達し、以下のエラーが発生するケースがあります。
TS2589: Type instantiation is excessively deep and possibly infinite.
発生原因と対策
TypeScriptは内部で型解析のループ数(Recursion Depth Limit)を制限しています。末尾の判定条件を甘く書くと無限ループとみなされるため、上記のように 「スラッシュありパターン」と「末尾単独パターン」を明確に分けて評価を終了させる記述(Tail Recursion 意識) が必須となります。
使用例と型補完の確認
function navigate<T extends string>(path: T, params: PathParams<T>) {
// 実装ロジック
}
// ⭕ 型補完が効き、キー名(userId, postId)のタイポを静的に検出
navigate('/users/:userId/posts/:postId', {
userId: '123',
postId: '456',
});
// ❌ 存在しないキーを指定するとコンパイルエラーになる
navigate('/users/:userId', {
invalidKey: '123', // Error: Object literal may only specify known properties
});