From 82b39d6f8243c97d84715efb03d4b755a5564518 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Tue, 26 Dec 2023 15:33:03 +0100 Subject: [PATCH 1/7] feat: allow search params with default values --- packages/react-router/src/fileRoute.ts | 20 ++++++ packages/react-router/src/link.tsx | 6 +- packages/react-router/src/route.ts | 96 +++++++++++++++++++++++--- packages/react-router/src/routeInfo.ts | 3 + 4 files changed, 114 insertions(+), 11 deletions(-) diff --git a/packages/react-router/src/fileRoute.ts b/packages/react-router/src/fileRoute.ts index a1cec00287e..c7008c0cb68 100644 --- a/packages/react-router/src/fileRoute.ts +++ b/packages/react-router/src/fileRoute.ts @@ -13,6 +13,8 @@ import { RootRouteId, TrimPathLeft, RouteConstraints, + ResolveFullSearchSchemaInput, + SearchSchemaInput, } from './route' import { Assign, Expand, IsAny } from './utils' @@ -85,7 +87,19 @@ export class FileRoute< constructor(public path: TFilePath) {} createRoute = < + TSearchSchemaInput extends RouteConstraints['TSearchSchema'] = {}, TSearchSchema extends RouteConstraints['TSearchSchema'] = {}, + TSearchSchemaUsed extends Record< + string, + any + > = TSearchSchemaInput extends SearchSchemaInput + ? TSearchSchemaInput + : TSearchSchema, + TFullSearchSchemaInput extends + RouteConstraints['TFullSearchSchema'] = ResolveFullSearchSchemaInput< + TParentRoute, + TSearchSchemaUsed + >, TFullSearchSchema extends RouteConstraints['TFullSearchSchema'] = ResolveFullSearchSchema< TParentRoute, @@ -115,7 +129,10 @@ export class FileRoute< TParentRoute, string, TPath, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, @@ -133,7 +150,10 @@ export class FileRoute< TFullPath, TFilePath, TId, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 7afb724f1f1..370908ab8d2 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -172,7 +172,7 @@ export type ParamOptions< TFrom, TTo, TResolved, - TParamVariant extends 'allParams' | 'fullSearchSchema', + TParamVariant extends 'allParams' | 'fullSearchSchemaInput', TFromParams = Expand['types'][TParamVariant]>, TToParams = TTo extends '' ? TFromParams @@ -187,7 +187,7 @@ export type ParamOptions< : MakeParamOption type MakeParamOption< - TParamVariant extends 'allParams' | 'fullSearchSchema', + TParamVariant extends 'allParams' | 'fullSearchSchemaInput', T, > = TParamVariant extends 'allParams' ? MakePathParamOptions @@ -200,7 +200,7 @@ export type SearchParamOptions< TFrom, TTo, TResolved, -> = ParamOptions +> = ParamOptions export type PathParamOptions< TRouteTree extends AnyRoute, diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index 4413a8f383e..f0bebb88fc1 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -23,6 +23,11 @@ export const rootRouteId = '__root__' as const export type RootRouteId = typeof rootRouteId export type AnyPathParams = {} +export const SearchSchemaSymbol: unique symbol = Symbol('@tanstack/router') +export type SearchSchemaInput = { + [SearchSchemaSymbol]: 'TSearchSchemaInput' +} + export type AnySearchSchema = {} export type AnyContext = {} @@ -56,7 +61,10 @@ export type RouteOptions< TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, + TSearchSchemaInput extends Record = {}, TSearchSchema extends Record = {}, + TSearchSchemaUsed extends Record = {}, + TFullSearchSchemaInput extends Record = TSearchSchemaUsed, TFullSearchSchema extends Record = TSearchSchema, TParams extends AnyPathParams = AnyPathParams, TAllParams extends AnyPathParams = TParams, @@ -68,7 +76,10 @@ export type RouteOptions< TParentRoute, TCustomId, TPath, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, @@ -88,7 +99,10 @@ export type BaseRouteOptions< TParentRoute extends AnyRoute = AnyRoute, TCustomId extends string = string, TPath extends string = string, + TSearchSchemaInput extends Record = {}, TSearchSchema extends Record = {}, + TSearchSchemaUsed extends Record = {}, + TFullSearchSchemaInput extends Record = TSearchSchemaUsed, TFullSearchSchema extends Record = TSearchSchema, TParams extends AnyPathParams = {}, TAllParams = ParamsFallback, @@ -98,7 +112,7 @@ export type BaseRouteOptions< TLoaderData extends any = unknown, > = RoutePathOptions & { getParentRoute: () => TParentRoute - validateSearch?: SearchSchemaValidator + validateSearch?: SearchSchemaValidator shouldReload?: | boolean | (( @@ -221,16 +235,16 @@ export type ParseParamsObj = { } // The parse type here allows a zod schema to be passed directly to the validator -export type SearchSchemaValidator = - | SearchSchemaValidatorObj - | SearchSchemaValidatorFn +export type SearchSchemaValidator = + | SearchSchemaValidatorObj + | SearchSchemaValidatorFn -export type SearchSchemaValidatorObj = { - parse?: SearchSchemaValidatorFn +export type SearchSchemaValidatorObj = { + parse?: SearchSchemaValidatorFn } -export type SearchSchemaValidatorFn = ( - searchObj: Record, +export type SearchSchemaValidatorFn = ( + searchObj: TInput, ) => TReturn export type DefinedPathParamWarning = @@ -287,10 +301,21 @@ export type InferFullSearchSchema = TRoute extends { ? TFullSearchSchema : {} +export type InferFullSearchSchemaInput = TRoute extends { + types: { + fullSearchSchemaInput: infer TFullSearchSchemaInput + } +} + ? TFullSearchSchemaInput + : {} + export type ResolveFullSearchSchema = Expand< Assign, TSearchSchema> > +export type ResolveFullSearchSchemaInput = + Expand, TSearchSchemaUsed>> + export interface AnyRoute extends Route< any, @@ -308,6 +333,9 @@ export interface AnyRoute any, any, any, + any, + any, + any, any > {} @@ -412,7 +440,18 @@ export class Route< TCustomId, TPath >, + TSearchSchemaInput extends RouteConstraints['TSearchSchema'] = {}, TSearchSchema extends RouteConstraints['TSearchSchema'] = {}, + TSearchSchemaUsed extends Record< + string, + any + > = TSearchSchemaInput extends SearchSchemaInput + ? Omit + : TSearchSchema, + TFullSearchSchemaInput extends Record< + string, + any + > = ResolveFullSearchSchemaInput, TFullSearchSchema extends RouteConstraints['TFullSearchSchema'] = ResolveFullSearchSchema< TParentRoute, @@ -442,7 +481,10 @@ export class Route< TParentRoute, TCustomId, TPath, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, @@ -475,7 +517,10 @@ export class Route< TParentRoute, TCustomId, TPath, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, @@ -502,7 +547,10 @@ export class Route< customId: TCustomId id: TId searchSchema: TSearchSchema + searchSchemaInput: TSearchSchemaInput + searchSchemaUsed: TSearchSchemaUsed fullSearchSchema: TFullSearchSchema + fullSearchSchemaInput: TFullSearchSchemaInput params: TParams allParams: TAllParams routeContext: TRouteContext @@ -521,7 +569,10 @@ export class Route< TParentRoute, TCustomId, TPath, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, @@ -590,7 +641,10 @@ export class Route< TFullPath, TCustomId, TId, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, TParams, TAllParams, @@ -656,7 +710,9 @@ export type AnyRootRoute = RootRoute export function rootRouteWithContext() { return < + TSearchSchemaInput extends Record = {}, TSearchSchema extends Record = {}, + TSearchSchemaUsed extends Record = {}, TRouteContext extends RouteContext = RouteContext, TLoaderDeps extends Record = {}, TLoaderData extends any = unknown, @@ -666,7 +722,10 @@ export function rootRouteWithContext() { AnyRoute, // TParentRoute RootRouteId, // TCustomId '', // TPath + TSearchSchemaInput, // TSearchSchemaInput TSearchSchema, // TSearchSchema + TSearchSchemaUsed, + TSearchSchemaUsed, //TFullSearchSchemaInput TSearchSchema, // TFullSearchSchema {}, // TParams {}, // TAllParams @@ -688,7 +747,9 @@ export function rootRouteWithContext() { } export class RootRoute< + TSearchSchemaInput extends Record = {}, TSearchSchema extends Record = {}, + TSearchSchemaUsed extends Record = {}, TRouteContext extends RouteContext = RouteContext, TRouterContext extends {} = {}, TLoaderDeps extends Record = {}, @@ -699,7 +760,10 @@ export class RootRoute< '/', // TFullPath string, // TCustomId RootRouteId, // TId + TSearchSchemaInput, // TSearchSchemaInput TSearchSchema, // TSearchSchema + TSearchSchemaUsed, + TSearchSchemaUsed, // TFullSearchSchemaInput TSearchSchema, // TFullSearchSchema {}, // TParams {}, // TAllParams @@ -717,7 +781,10 @@ export class RootRoute< AnyRoute, // TParentRoute RootRouteId, // TCustomId '', // TPath + TSearchSchemaInput, // TSearchSchemaInput TSearchSchema, // TSearchSchema + TSearchSchemaUsed, + TSearchSchemaUsed, // TFullSearchSchemaInput TSearchSchema, // TFullSearchSchema {}, // TParams {}, // TAllParams @@ -821,7 +888,14 @@ export type ErrorRouteComponent = RouteComponent export class NotFoundRoute< TParentRoute extends AnyRootRoute, + TSearchSchemaInput extends Record = {}, TSearchSchema extends RouteConstraints['TSearchSchema'] = {}, + TSearchSchemaUsed extends RouteConstraints['TSearchSchema'] = {}, + TFullSearchSchemaInput extends + RouteConstraints['TFullSearchSchema'] = ResolveFullSearchSchemaInput< + TParentRoute, + TSearchSchemaUsed + >, TFullSearchSchema extends RouteConstraints['TFullSearchSchema'] = ResolveFullSearchSchema< TParentRoute, @@ -844,7 +918,10 @@ export class NotFoundRoute< '/404', '404', '404', + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, {}, {}, @@ -862,7 +939,10 @@ export class NotFoundRoute< TParentRoute, string, string, + TSearchSchemaInput, TSearchSchema, + TSearchSchemaUsed, + TFullSearchSchemaInput, TFullSearchSchema, {}, {}, diff --git a/packages/react-router/src/routeInfo.ts b/packages/react-router/src/routeInfo.ts index 575946a06d5..025e17ddb5e 100644 --- a/packages/react-router/src/routeInfo.ts +++ b/packages/react-router/src/routeInfo.ts @@ -21,6 +21,9 @@ export type ParseRouteChildren = any, any, any, + any, + any, + any, infer TChildren, any > From 67be0598a694c2514790181c5a776b0f7934f7e3 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Tue, 2 Jan 2024 22:52:42 +0100 Subject: [PATCH 2/7] example for default search params --- .../basic-default-search-params/.gitignore | 5 + .../basic-default-search-params/README.md | 6 + .../basic-default-search-params/index.html | 13 + .../basic-default-search-params/package.json | 25 ++ .../basic-default-search-params/src/main.tsx | 224 ++++++++++++++++++ .../tsconfig.dev.json | 12 + .../basic-default-search-params/tsconfig.json | 7 + .../vite.config.js | 7 + 8 files changed, 299 insertions(+) create mode 100644 examples/react/basic-default-search-params/.gitignore create mode 100644 examples/react/basic-default-search-params/README.md create mode 100644 examples/react/basic-default-search-params/index.html create mode 100644 examples/react/basic-default-search-params/package.json create mode 100644 examples/react/basic-default-search-params/src/main.tsx create mode 100644 examples/react/basic-default-search-params/tsconfig.dev.json create mode 100644 examples/react/basic-default-search-params/tsconfig.json create mode 100644 examples/react/basic-default-search-params/vite.config.js diff --git a/examples/react/basic-default-search-params/.gitignore b/examples/react/basic-default-search-params/.gitignore new file mode 100644 index 00000000000..d451ff16c10 --- /dev/null +++ b/examples/react/basic-default-search-params/.gitignore @@ -0,0 +1,5 @@ +node_modules +.DS_Store +dist +dist-ssr +*.local diff --git a/examples/react/basic-default-search-params/README.md b/examples/react/basic-default-search-params/README.md new file mode 100644 index 00000000000..115199d292c --- /dev/null +++ b/examples/react/basic-default-search-params/README.md @@ -0,0 +1,6 @@ +# Example + +To run this example: + +- `npm install` or `yarn` +- `npm start` or `yarn start` diff --git a/examples/react/basic-default-search-params/index.html b/examples/react/basic-default-search-params/index.html new file mode 100644 index 00000000000..2e8ce205fc4 --- /dev/null +++ b/examples/react/basic-default-search-params/index.html @@ -0,0 +1,13 @@ + + + + + + Vite App + + + +
+ + + diff --git a/examples/react/basic-default-search-params/package.json b/examples/react/basic-default-search-params/package.json new file mode 100644 index 00000000000..6ad42014237 --- /dev/null +++ b/examples/react/basic-default-search-params/package.json @@ -0,0 +1,25 @@ +{ + "name": "tanstack-router-react-example-basic-default-search-params", + "version": "0.0.0", + "private": true, + "scripts": { + "dev": "vite --port=3001", + "build": "vite build", + "serve": "vite preview", + "start": "vite" + }, + "dependencies": { + "@tanstack/react-query": "^5.4.3", + "@tanstack/react-router": "1.0.8", + "@tanstack/router-devtools": "1.0.8", + "@vitejs/plugin-react": "^1.1.3", + "axios": "^1.1.3", + "react": "^18.2.0", + "react-dom": "^18.2.0", + "vite": "^2.8.6" + }, + "devDependencies": { + "@types/react": "^18.2.41", + "@types/react-dom": "^18.2.17" + } +} diff --git a/examples/react/basic-default-search-params/src/main.tsx b/examples/react/basic-default-search-params/src/main.tsx new file mode 100644 index 00000000000..b79a6398301 --- /dev/null +++ b/examples/react/basic-default-search-params/src/main.tsx @@ -0,0 +1,224 @@ +import React from 'react' +import ReactDOM from 'react-dom/client' +import { + Outlet, + RouterProvider, + Link, + Route, + ErrorComponent, + Router, + RootRoute, + ErrorRouteProps, + NotFoundRoute, + SearchSchemaInput, +} from '@tanstack/react-router' +import { TanStackRouterDevtools } from '@tanstack/router-devtools' +import axios from 'axios' +import { z } from 'zod' + +type PostType = { + id: number + title: string + body: string +} + +const fetchPosts = async () => { + console.log('Fetching posts...') + await new Promise((r) => setTimeout(r, 300)) + return axios + .get('https://jsonplaceholder.typicode.com/posts') + .then((r) => r.data.slice(0, 10)) +} + +const fetchPost = async (postId: number) => { + console.log(`Fetching post with id ${postId}...`) + await new Promise((r) => setTimeout(r, 300)) + const post = await axios + .get(`https://jsonplaceholder.typicode.com/posts/${postId}`) + .catch((err) => { + if (err.response.status === 404) { + throw new NotFoundError(`Post with id "${postId}" not found!`) + } + throw err + }) + .then((r) => r.data) + + return post +} + +const rootRoute = new RootRoute({ + component: RootComponent, +}) + +function RootComponent() { + return ( +
+
+ + Home + {' '} + + Posts + +
+ + +
+ ) +} +const indexRoute = new Route({ + getParentRoute: () => rootRoute, + path: '/', + component: IndexComponent, +}) + +function IndexComponent() { + return ( +
+

Welcome Home!

+
+ ) +} + +const postsRoute = new Route({ + getParentRoute: () => rootRoute, + path: 'posts', + loader: () => fetchPosts(), + component: PostsComponent, +}) + +function PostsComponent() { + const posts = postsRoute.useLoaderData() + + return ( +
+
+ {posts.map((post, index) => { + return ( +
+ +
{post.title.substring(0, 20)}
+ +
+ ) + })} +
+ +
+ ) +} + +const postsIndexRoute = new Route({ + getParentRoute: () => postsRoute, + path: '/', + component: PostsIndexComponent, +}) + +function PostsIndexComponent() { + return
Select a post.
+} + +class NotFoundError extends Error {} + +const postRoute = new Route({ + getParentRoute: () => postsRoute, + path: 'post', + validateSearch: ( + input: { + postId: number + color?: 'white' | 'red' | 'green' + } & SearchSchemaInput, + ) => + z + .object({ + postId: z.number().catch(1), + color: z.enum(['white', 'red', 'green']).catch('white'), + }) + .parse(input), + loaderDeps: ({ search: { postId } }) => ({ + postId, + }), + errorComponent: PostErrorComponent, + loader: ({ deps: { postId } }) => fetchPost(postId), + component: PostComponent, +}) + +function PostErrorComponent({ error }: ErrorRouteProps) { + if (error instanceof NotFoundError) { + return
{error.message}
+ } + + return +} + +function PostComponent() { + const post = postRoute.useLoaderData() + const { color } = postRoute.useSearch() + return ( +
+

{post.title}

+
+
{post.body}
+
+ ) +} + +const notFoundRoute = new NotFoundRoute({ + getParentRoute: () => rootRoute, + component: NotFound, +}) + +function NotFound() { + return ( +
+

404 - Not Found

+
+ ) +} + +const routeTree = rootRoute.addChildren([ + postsRoute.addChildren([postRoute, postsIndexRoute]), + indexRoute, +]) + +// Set up a Router instance +const router = new Router({ + routeTree, + notFoundRoute, + defaultPreload: 'intent', + defaultStaleTime: 5000, +}) + +// Register things for typesafety +declare module '@tanstack/react-router' { + interface Register { + router: typeof router + } +} + +const rootElement = document.getElementById('app')! + +if (!rootElement.innerHTML) { + const root = ReactDOM.createRoot(rootElement) + + root.render() +} diff --git a/examples/react/basic-default-search-params/tsconfig.dev.json b/examples/react/basic-default-search-params/tsconfig.dev.json new file mode 100644 index 00000000000..c09bc865f06 --- /dev/null +++ b/examples/react/basic-default-search-params/tsconfig.dev.json @@ -0,0 +1,12 @@ +{ + "composite": true, + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "./build/types" + }, + "files": ["src/main.tsx"], + "include": [ + "src" + // "__tests__/**/*.test.*" + ] +} diff --git a/examples/react/basic-default-search-params/tsconfig.json b/examples/react/basic-default-search-params/tsconfig.json new file mode 100644 index 00000000000..0453a66fb9d --- /dev/null +++ b/examples/react/basic-default-search-params/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "strict": true, + "esModuleInterop": true, + "jsx": "react" + } +} diff --git a/examples/react/basic-default-search-params/vite.config.js b/examples/react/basic-default-search-params/vite.config.js new file mode 100644 index 00000000000..5a33944a9b4 --- /dev/null +++ b/examples/react/basic-default-search-params/vite.config.js @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [react()], +}) From 67d459853e4311b5fdaa576343a55c633a3b18bb Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Tue, 2 Jan 2024 22:57:02 +0100 Subject: [PATCH 3/7] removed symbol usage --- packages/react-router/src/route.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index f0bebb88fc1..fc307075e70 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -23,9 +23,8 @@ export const rootRouteId = '__root__' as const export type RootRouteId = typeof rootRouteId export type AnyPathParams = {} -export const SearchSchemaSymbol: unique symbol = Symbol('@tanstack/router') export type SearchSchemaInput = { - [SearchSchemaSymbol]: 'TSearchSchemaInput' + __TSearchSchemaInput__: 'TSearchSchemaInput' } export type AnySearchSchema = {} @@ -446,7 +445,7 @@ export class Route< string, any > = TSearchSchemaInput extends SearchSchemaInput - ? Omit + ? Omit : TSearchSchema, TFullSearchSchemaInput extends Record< string, From 00954c06036aa1c607e89c88d255e606a14cc3c6 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Tue, 2 Jan 2024 23:04:57 +0100 Subject: [PATCH 4/7] avoid repeating magic string --- packages/react-router/src/route.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index fc307075e70..510f64b8a0b 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -445,7 +445,7 @@ export class Route< string, any > = TSearchSchemaInput extends SearchSchemaInput - ? Omit + ? Omit : TSearchSchema, TFullSearchSchemaInput extends Record< string, From 8fc2c047b698695a2931b7ec4a50afb9d9ece2f8 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Wed, 3 Jan 2024 01:11:02 +0100 Subject: [PATCH 5/7] docs: update API reference w.r.t. TSearchSchemaInput --- docs/api/router/RouteOptionsType.md | 1 + docs/api/router/SearchParamOptionsType.md | 2 +- docs/api/router/SearchSchemaInputType.md | 7 +++++++ 3 files changed, 9 insertions(+), 1 deletion(-) create mode 100644 docs/api/router/SearchSchemaInputType.md diff --git a/docs/api/router/RouteOptionsType.md b/docs/api/router/RouteOptionsType.md index 2154c2bae5c..7dd13cd7cdc 100644 --- a/docs/api/router/RouteOptionsType.md +++ b/docs/api/router/RouteOptionsType.md @@ -31,6 +31,7 @@ The `RouteOptions` type is used to describe the options that can be used when cr - Type: `(rawSearchParams: unknown) => TSearchSchema` - Optional - A function that will be called when this route is matched and passed the raw search params from the current location and return valid parsed search params. If this function throws, the route will be put into an error state and the error will be thrown during render. If this function does not throw, its return value will be used as the route's search params and the return type will be inferred into the rest of the router. +- Optionally, the parameter type can be tagged with the `SearchSchemaInput` type like this: `(searchParams: TSearchSchemaInput & SearchSchemaInput) => TSearchSchema`. If this tag is present, `TSearchSchemaInput` will be used to type the `search` property of `` and `navigate()` **instead of** `TSearchSchema`. The difference between `TSearchSchemaInput` and `TSearchSchema` can be useful, for example, to express optional search parameters. #### `parseParams` diff --git a/docs/api/router/SearchParamOptionsType.md b/docs/api/router/SearchParamOptionsType.md index 8bcc7a5cb8c..cefe40b3f0a 100644 --- a/docs/api/router/SearchParamOptionsType.md +++ b/docs/api/router/SearchParamOptionsType.md @@ -10,7 +10,7 @@ The `SearchParamOptions` type is used to describe how search params can be provi type SearchParamOptions = { search?: | true - | Record + | TToSearch | ((prev: TFromSearch) => TToSearch) } ``` diff --git a/docs/api/router/SearchSchemaInputType.md b/docs/api/router/SearchSchemaInputType.md new file mode 100644 index 00000000000..1cc1b01a09c --- /dev/null +++ b/docs/api/router/SearchSchemaInputType.md @@ -0,0 +1,7 @@ +--- +id: SearchSchemaInputType +title: `SearchSchemaInput` type +--- + + +The `SearchSchemaInput` type is used to tag the input type of a `validateSearch` method to signalize to TanStack router that its parameter type `TSearchSchemaInput` shall be used to type the search param of `` and `navigate()`. \ No newline at end of file From 09d54ab089df45c7f00dab5e4178c9b65e30bd3a Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Wed, 3 Jan 2024 01:11:34 +0100 Subject: [PATCH 6/7] fix search param types --- packages/react-router/src/link.tsx | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/packages/react-router/src/link.tsx b/packages/react-router/src/link.tsx index 370908ab8d2..61551682ea9 100644 --- a/packages/react-router/src/link.tsx +++ b/packages/react-router/src/link.tsx @@ -167,18 +167,21 @@ export type ToSubOptions< type ParamsReducer = TTo | ((current: TFrom) => TTo) +type ParamVariant = 'PATH' | 'SEARCH'; export type ParamOptions< TRouteTree extends AnyRoute, TFrom, TTo, TResolved, - TParamVariant extends 'allParams' | 'fullSearchSchemaInput', - TFromParams = Expand['types'][TParamVariant]>, + TParamVariant extends ParamVariant, + TFromRouteType extends 'allParams' | 'fullSearchSchema' = TParamVariant extends 'PATH' ? 'allParams' : 'fullSearchSchema', + TToRouteType extends 'allParams' | 'fullSearchSchemaInput' = TParamVariant extends 'PATH' ? 'allParams' : 'fullSearchSchemaInput', + TFromParams = Expand['types'][TFromRouteType]>, TToParams = TTo extends '' ? TFromParams : never extends TResolved - ? Expand['types'][TParamVariant]> - : Expand['types'][TParamVariant]>, + ? Expand['types'][TToRouteType]> + : Expand['types'][TToRouteType]>, TReducer = ParamsReducer, > = Expand>> extends never ? Partial> @@ -187,9 +190,9 @@ export type ParamOptions< : MakeParamOption type MakeParamOption< - TParamVariant extends 'allParams' | 'fullSearchSchemaInput', + TParamVariant extends ParamVariant, T, -> = TParamVariant extends 'allParams' +> = TParamVariant extends 'PATH' ? MakePathParamOptions : MakeSearchParamOptions type MakeSearchParamOptions = { search: T } @@ -200,14 +203,14 @@ export type SearchParamOptions< TFrom, TTo, TResolved, -> = ParamOptions +> = ParamOptions export type PathParamOptions< TRouteTree extends AnyRoute, TFrom, TTo, TResolved, -> = ParamOptions +> = ParamOptions export type ToPathOption< TRouteTree extends AnyRoute = AnyRoute, From 80ce6392233c971a19e0d0ae849bf4cc5be00597 Mon Sep 17 00:00:00 2001 From: Manuel Schiller Date: Wed, 3 Jan 2024 03:56:55 +0100 Subject: [PATCH 7/7] fix RootRoute --- packages/react-router/src/route.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/react-router/src/route.ts b/packages/react-router/src/route.ts index 510f64b8a0b..8b28a7cb051 100644 --- a/packages/react-router/src/route.ts +++ b/packages/react-router/src/route.ts @@ -705,7 +705,7 @@ export class Route< } } -export type AnyRootRoute = RootRoute +export type AnyRootRoute = RootRoute export function rootRouteWithContext() { return < @@ -740,7 +740,13 @@ export function rootRouteWithContext() { | 'parseParams' | 'stringifyParams' >, - ): RootRoute => { + ): RootRoute< + TSearchSchemaInput, + TSearchSchema, + TSearchSchemaUsed, + TRouteContext, + TRouterContext + > => { return new RootRoute(options) as any } }