From f334dfda0bdd2f210d8713f354d4e6d9b91d634b Mon Sep 17 00:00:00 2001 From: Mathieu Acthernoene Date: Mon, 22 Jul 2024 16:26:47 +0200 Subject: [PATCH] Fix params type when the route name is an union (#50) --- docs/docs/utility-types.md | 21 +++++++++++++++++++++ docs/sidebars.js | 1 + package.json | 2 +- src/createRouter.ts | 5 +++-- src/index.ts | 2 +- src/types.ts | 29 ++++++++++++++++++++++++++++- 6 files changed, 55 insertions(+), 5 deletions(-) create mode 100644 docs/docs/utility-types.md diff --git a/docs/docs/utility-types.md b/docs/docs/utility-types.md new file mode 100644 index 00000000..cc780019 --- /dev/null +++ b/docs/docs/utility-types.md @@ -0,0 +1,21 @@ +--- +title: Utility types +sidebar_label: Utility types +--- + +To easily extract routes types, use `InferRoutes`: + +```tsx {1,9} +import { createRouter, InferRoutes } from "@swan-io/chicane"; + +export const Router = createRouter({ + UserList: "/users", + UserDetail: "/users/:userId", +}); + +// A map of RouteName and its associated RouteParams +type Routes = InferRoutes; + +export type RouteName = keyof Routes; +export type RouteParams = Routes[T]; +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index c2e2a02b..db1a91a2 100644 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -28,6 +28,7 @@ const sidebars = { "matching-some-routes", "linking-to-a-route", "server-side-rendering", + "utility-types", ], }, { diff --git a/package.json b/package.json index 909c7d32..342c3eb4 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@swan-io/chicane", - "version": "2.0.0", + "version": "2.1.0", "license": "MIT", "description": "A simple and safe router for React and TypeScript", "author": "Mathieu Acthernoene ", diff --git a/src/createRouter.ts b/src/createRouter.ts index acad7c83..29c8931f 100644 --- a/src/createRouter.ts +++ b/src/createRouter.ts @@ -22,6 +22,7 @@ import { ParseRoutes, PrependBasePath, RouteObject, + UnionToIntersection, } from "./types"; export const createRouter = < @@ -138,12 +139,12 @@ export const createRouter = < const push = ( routeName: RouteName, - ...params: ParamsArg + ...params: ParamsArg> ): void => pushUnsafe(matchToUrl(matchers[routeName], first(params))); const replace = ( routeName: RouteName, - ...params: ParamsArg + ...params: ParamsArg> ): void => replaceUnsafe(matchToUrl(matchers[routeName], first(params))); return { diff --git a/src/index.ts b/src/index.ts index 41c0357a..15174b7a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -10,7 +10,7 @@ export { } from "./history"; export { decodeSearch, encodeSearch } from "./search"; export { ServerUrlProvider } from "./server"; -export type { Location, Search } from "./types"; +export type { InferRoutes, Location, Search } from "./types"; export { useBlocker } from "./useBlocker"; export { useFocusReset } from "./useFocusReset"; export { useLinkProps } from "./useLinkProps"; diff --git a/src/types.ts b/src/types.ts index ab641e47..8f903577 100644 --- a/src/types.ts +++ b/src/types.ts @@ -171,6 +171,13 @@ type NonOptionalProperties = Exclude< undefined >; +// https://github.com/microsoft/TypeScript/issues/13298#issuecomment-1610361208 +export type UnionToIntersection = ( + Union extends never ? never : (_: Union) => never +) extends (_: infer Intersection) => void + ? Intersection + : never; + export type ParamsArg = Params extends Record ? [] @@ -180,6 +187,26 @@ export type ParamsArg = export type GetCreateURLFns> = { [RouteName in keyof RoutesParams]: ( - ...params: ParamsArg + ...params: ParamsArg> ) => string; }; + +// User land helpers + +type RouteLike = { + name: string; + params: Params; +}; + +type RouterLike = { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + getRoute: (...args: any[]) => RouteLike | undefined; +}; + +type RemapRoute = { + [N in Route["name"]]: Extract["params"]; +}; + +export type InferRoutes = RemapRoute< + NonNullable> +>;