Open jaens opened 3 months ago
ToOptions
rely on TS inferring types in e.g. useNavigate
, so you can't use them as stand-alone types.
If you really need this kind of flexibility (and want give up on a lot of type-safety e.g. for search/path params) you can store the to
prop as a string and pass it in:
let to = '/';
useLinkProps({to});
I'm sorry, but that's not really a solution? I don't think this should be closed?
Are you saying there's no way to store links separately in a type safe way? That seems like... bad.
Based on my experience, this should definitely be possible.
A simple solution would be to have a link()
function like in the CodeSandbox that just returns the link as is, but would infer types. It's the standard TypeScript pattern of using an identity-function for type inference.
The same pattern is used in eg. Tanstack Query for queryOptions()
.
I'm just not sure what the type of such a link
-function would be, given the visible type declaration in the public API is basically unapproachable without documentation:
export type UseLinkPropsOptions<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TFrom extends RoutePaths<TRouteTree> | string = string, TTo extends string = '', TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom, TMaskTo extends string = ''> = .....
What is "a link" exactly that you want to store in a "type safe way"? just the to
part?
Everything that's in ToOptions
, including parameters. Anything that can be passed to both useNavigate()
and useLinkProps()
.
The example does demonstrate that at least UseLinkPropsOptions
is usable as a stand-alone type and works when passed to useLinkProps()
, the only issue is making an object literal conform to that type, which is merely some sort of problem with inference...
I would actually be happy with just a single function that can make a literal turn into any one (pick what you like best) of the *Options
types, since that's where the main benefit of type safety lies.
Anything else can be achieved by casting between the *Options
types and does not really sacrifice the main benefit of type safety, which is strongly typing the path and params.
Even if the type can be inferred by such a helper function, how would you pass those links using props etc? You would have to spell out the types there again. Can you give some concrete examples?
function A() {
const myLink = linkHelper({to: '/foo/$fooId', params: { fooId: 'bar123'}});
// what should the type of myLink now be?
return <B link={myLink} />
}
interface BProps {
link: ??? // what to put here?
}
function B({link} : BProps) {
/// ...
}
Clearly, eg. ToOptions
should theoretically be passable to both useNavigate
and useLinkProps
, since both functions work with anything that practically conforms to that type. Any other behaviour would be a bug in the types.
The answer then, of course, is link: ToOptions
.
If that's somehow too complicated to fix, UseLinkPropsOptions
would also be usable. It already works as a type in the CodeSandbox above.
As a practical use case, a very simple and common example would be eg. in a design system, where you would have a component wrapping a link:
function MyLinkLike({ link, renderAsButton, children } : { link: ???, ... }) {
const navigate = useNavigate();
return renderAsButton ?
<Button onPress={() => navigate(link)} /> :
<SomeOtherComponent><Link {...link}>{children}</Link></SomeOtherComponent>;
}
The example code is obviously simplified and bad from various perspectives, but it demonstrates the basic issue.
@schiller-manuel I believe this is still a valid issue/problem (so this shouldn't be closed?)
Design systems often use generic link representations.
Here's my current "implementation" that "works":
export function link<
TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = "",
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
TMaskTo extends string = "",
>(options: UseLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>) {
return options as UseLinkPropsOptions;
}
const _testLink: UseLinkPropsOptions = link({ to: "/" });
I can contribute a solution if there is consensus on how to move forward.
In my case, I have a reusable component that, once the user is done with it, needs to be able to route different places based on where it started. I pass a prop I call closeTo
into this component, and in its submit function it calls navigate({ to: props.closeTo })
. I want to make sure that as more people work on this project with me, they get type-safe suggestions and warnings to ensure they pass a path into my component's closeTo
prop that's a valid route in the routeTree
. Having a quick and easy single exposed type that's even as simple as a union of all paths in the route tree would be awesome! Though I totally understand folks who would love type-safe params etc as well.
I had the same issue and after a bit of playing around I found this works:
import { RouterProvider, createRouter, Link, ToOptions } from '@tanstack/react-router';
import { routeTree } from './routeTree.gen';
export const router = createRouter({
defaultPreload: 'intent',
routeTree
});
const MyLink = ({ link }: { link: ToOptions<typeof router> }) => {
return <Link {...link}>Testing</Link>
}
Describe the bug
It's not possible to use the
ToOptions
,UseLinkPropsOptions
etc. types to store a link in a variable. It seems to always result in a type error. This is in the "basic" example.(passing the exact same object to eg.
useLinkProps()
works fine)I'm not sure what the type of a link is supposed to be, as I can't find any other possibilities anywhere in the documentation? Links do need to be occasionally stored in variables...
(note that using
as const
to not have a type at all is not a solution, in this simple example it would be, but practically this type also needs to occur in parameters or props, which always need an explicit type.)Your Example Website or App
CodeSandbox example (based on "basic")
(edited, initial link was broken due to CodeSandbox bug...)
Steps to Reproduce the Bug or Issue
Check the Codesandbox.
Expected behavior
There's some type I can use to store links that I can then pass to eg.
useNavigate()
oruseLinkProps()
Screenshots or Videos
No response
Platform
all
Additional context
No response