T
TanStack4mo ago
fascinating-indigo

Search-Typing issue

Hey 👋🏽 . I love tanstack router. Does somebody know why following works:
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
import { Login } from "shared-features/login";

export const Route = createFileRoute("/login")({
validateSearch: z.object({
redirect: z.string().catch("/app"),
}),
component: LoginComponent,
});

function LoginComponent() {
const navigate = Route.useNavigate();
const redirect = Route.useSearch({ select: (search) => search.redirect });

const handleRedirect = () => {
navigate({
to: redirect,
replace: true,
});
};

return (
<Login title="Hello" onLogin={handleRedirect}/>
);
}
import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
import { Login } from "shared-features/login";

export const Route = createFileRoute("/login")({
validateSearch: z.object({
redirect: z.string().catch("/app"),
}),
component: LoginComponent,
});

function LoginComponent() {
const navigate = Route.useNavigate();
const redirect = Route.useSearch({ select: (search) => search.redirect });

const handleRedirect = () => {
navigate({
to: redirect,
replace: true,
});
};

return (
<Login title="Hello" onLogin={handleRedirect}/>
);
}
but following not:
import { createFileRoute } from "@tanstack/react-router";
import { Login } from "shared-features/login";

type LoginSearch = {
redirect: string;
};

export const Route = createFileRoute("/login")({
validateSearch: (search): LoginSearch => {
return {
redirect: (search.redirect as string) || "/app",
};
},
component: LoginComponent,
});

function LoginComponent() {
const navigate = Route.useNavigate();
const redirect = Route.useSearch({ select: (search) => search.redirect });

const handleRedirect = () => {
navigate({
to: redirect,
replace: true,
});
};

return (
<Login title="Hello" onLogin={handleRedirect}/>
);
}
import { createFileRoute } from "@tanstack/react-router";
import { Login } from "shared-features/login";

type LoginSearch = {
redirect: string;
};

export const Route = createFileRoute("/login")({
validateSearch: (search): LoginSearch => {
return {
redirect: (search.redirect as string) || "/app",
};
},
component: LoginComponent,
});

function LoginComponent() {
const navigate = Route.useNavigate();
const redirect = Route.useSearch({ select: (search) => search.redirect });

const handleRedirect = () => {
navigate({
to: redirect,
replace: true,
});
};

return (
<Login title="Hello" onLogin={handleRedirect}/>
);
}
The only difference is that in first version I use zod to validate the search, and in the second I do it myself.
7 Replies
fascinating-indigo
fascinating-indigoOP4mo ago
For the second version I get:
Argument of type '{ to: string; replace: true; }' is not assignable to parameter of type 'NavigateOptions<RouterCore<Route<any, "/", "/", string, "__root__", undefined, {}, { queryClient: QueryClient; }, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes>, "never", false, RouterHistory, Record<...>>, "/login", string, "/login", "">'.
Property 'search' is missing in type '{ to: string; replace: true; }' but required in type 'MakeRequiredSearchParams<RouterCore<Route<any, "/", "/", string, "__root__", undefined, {}, { queryClient: QueryClient; }, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes>, "never", false, RouterHistory, Record<...>>, "/login", string>'.ts(2345)
link.d.ts(140, 5): 'search' is declared here.
Argument of type '{ to: string; replace: true; }' is not assignable to parameter of type 'NavigateOptions<RouterCore<Route<any, "/", "/", string, "__root__", undefined, {}, { queryClient: QueryClient; }, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes>, "never", false, RouterHistory, Record<...>>, "/login", string, "/login", "">'.
Property 'search' is missing in type '{ to: string; replace: true; }' but required in type 'MakeRequiredSearchParams<RouterCore<Route<any, "/", "/", string, "__root__", undefined, {}, { queryClient: QueryClient; }, AnyContext, AnyContext, {}, undefined, RootRouteChildren, FileRouteTypes>, "never", false, RouterHistory, Record<...>>, "/login", string>'.ts(2345)
link.d.ts(140, 5): 'search' is declared here.
Thanks
generous-apricot
generous-apricot4mo ago
I think you would need to manually specify the input type of the search for the second one. it can be inferred from the zod Schema automatically.
generous-apricot
generous-apricot4mo ago
RouteOptions type | TanStack Router React Docs
The RouteOptions type is used to describe the options that can be used when creating a route. RouteOptions properties The RouteOptions type accepts an object with the following properties: getParentRo...
fascinating-indigo
fascinating-indigoOP4mo ago
You mean this?
validateSearch: (search: LoginSearch): LoginSearch => {
return {
redirect: search.redirect || "/app",
};
}
validateSearch: (search: LoginSearch): LoginSearch => {
return {
redirect: search.redirect || "/app",
};
}
I typed it already in my example, didn't I? If I hover over the redirect-var in the LoginComponent, I get const redirect: string, so it should be typed correctly. And the error message seems not related to the search-typing. thanks
generous-apricot
generous-apricot4mo ago
then please provide a complete minimal example
stormy-gold
stormy-gold4mo ago
You need to add SearchSchemaInput and mark the redirect property as optional:
validateSearch: (search: Partial<LoginSearch> & SearchSchemaInput)
: LoginSearch => { /* ... */ }
validateSearch: (search: Partial<LoginSearch> & SearchSchemaInput)
: LoginSearch => { /* ... */ }
fascinating-indigo
fascinating-indigoOP4mo ago
Thanks @shelly! @Manuel Schiller The only thing I had to change in my 2nd example was making the redirect-prop optional.
type LoginSearch = {
redirect?: string;
};

// ...
validateSearch: (search: LoginSearch): LoginSearch => {
return {
redirect: search.redirect ?? "/app",
};
},
// ...
type LoginSearch = {
redirect?: string;
};

// ...
validateSearch: (search: LoginSearch): LoginSearch => {
return {
redirect: search.redirect ?? "/app",
};
},
// ...

Did you find this page helpful?