/** * Extract required parameter names from route path at type level * * @example * ExtractRequiredParams<'/users/:id/posts/:postId'> = { id: string; postId: string } * ExtractRequiredParams<'/users'> = Record */ export type ExtractRequiredParams = TPath extends `${infer _Start}:${infer _Param}?/${infer Rest}` ? ExtractRequiredParams<`/${Rest}`> : TPath extends `${infer _Start}:${infer Param}/${infer Rest}` ? { [K in Param]: string; } & ExtractRequiredParams<`/${Rest}`> : TPath extends `${infer _Start}:${infer _Param}?` ? Record : TPath extends `${infer _Start}:${infer Param}` ? { [K in Param]: string; } : Record; /** * Extract optional parameter names from route path * * @example * ExtractOptionalParams<'/search/:query?'> = { query?: string } */ export type ExtractOptionalParams = TPath extends `${infer _Start}:${infer Param}?/${infer Rest}` ? { [K in Param]?: string; } & ExtractOptionalParams<`/${Rest}`> : TPath extends `${infer _Start}:${infer Param}?` ? { [K in Param]?: string; } : Record; /** * Combined parameter type for a route - merges required and optional params * * @example * RouteParams<'/users/:id/posts/:postId?'> = { id: string; postId?: string } */ export type RouteParams = ExtractRequiredParams & ExtractOptionalParams; /** * Check if a path has any parameters */ export type HasParams = keyof RouteParams extends never ? false : true; /** * Get the params type for a given path, with proper handling for parameterless routes */ export type ParamsFor = keyof RouteParams extends never ? undefined : RouteParams;