Created
September 13, 2025 10:39
-
-
Save zbeyens/2c6a1ee5a07a981a3b293d1308362918 to your computer and use it in GitHub Desktop.
Typed hooks for [email protected]
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import { AppRoutes, ParamMap } from '.next/types/routes'; | |
| import { Route } from 'next'; | |
| import { useParams, usePathname } from 'next/navigation'; | |
| /** Get the current pathname as a typed route. */ | |
| export const useStaticRoute = <R extends Route = Route>() => { | |
| return usePathname() as R; | |
| }; | |
| /** Get typed route parameters for a specific app route. */ | |
| export const useTParams = <AppRoute extends AppRoutes>() => { | |
| const params = useParams<ParamMap[AppRoute]>(); | |
| return params; | |
| }; | |
| type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends (k: infer I) => void | |
| ? I | |
| : never; | |
| type NonEmptyParams<T> = | |
| T extends Record<string, string> ? (keyof T extends never ? never : T) : never; | |
| type RoutesStartingWith<Route extends string> = { | |
| [K in keyof ParamMap]: K extends `${Route}${string}` ? K : never; | |
| }[keyof ParamMap]; | |
| type IsExactRoute<Route extends string> = Route extends keyof ParamMap ? true : false; | |
| type PrefixRoutesExcludingExact<Route extends string> = { | |
| [K in keyof ParamMap]: K extends `${Route}${string}` ? (K extends Route ? never : K) : never; | |
| }[keyof ParamMap]; | |
| type UnifiedParamsForPrefix<Route extends string> = | |
| IsExactRoute<Route> extends true | |
| ? Route extends keyof ParamMap | |
| ? ParamMap[Route] & | |
| Partial<UnionToIntersection<NonEmptyParams<ParamMap[PrefixRoutesExcludingExact<Route>]>>> | |
| : never | |
| : Partial<UnionToIntersection<NonEmptyParams<ParamMap[RoutesStartingWith<Route>]>>>; | |
| type UnifiedParams = Partial<UnionToIntersection<NonEmptyParams<ParamMap[keyof ParamMap]>>>; | |
| /** | |
| * Get unified route parameters for layout components. | |
| * | |
| * When called without a generic, returns all possible route parameters as optional. When called | |
| * with a route generic, returns: | |
| * | |
| * - Required parameters for exact route matches | |
| * - Optional parameters for routes that start with the same prefix | |
| * | |
| * Works best with unique parameter names across routes (e.g., userId vs id). | |
| * | |
| * @example | |
| * ```tsx | |
| * // Get all possible params as optional | |
| * const params = useLayoutParams(); | |
| * | |
| * // Get typed params for user routes | |
| * const params = useLayoutParams<'/users/[userId]'>(); | |
| * // Result: { userId: string; profileId?: string; ... } | |
| * ```; | |
| */ | |
| export function useLayoutParams(): UnifiedParams; | |
| export function useLayoutParams<Route extends string>(): UnifiedParamsForPrefix<Route>; | |
| export function useLayoutParams() { | |
| const params = useParams(); | |
| return params as any; | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment