T
TanStack2y ago
rare-sapphire

Strictly typed Link wrapper

I'd like to create a strictly typed <Link> wrapper like the following example:
import { LinkProps, Link } from '@tanstack/react-router';

const CustomLink = ({ linkProps, children }: { linkProps: LinkProps; children: ReactNode; }) => {
return <Link {...linkProps}>{children}</Link>
};
import { LinkProps, Link } from '@tanstack/react-router';

const CustomLink = ({ linkProps, children }: { linkProps: LinkProps; children: ReactNode; }) => {
return <Link {...linkProps}>{children}</Link>
};
But this is unfortunately not strictly typed. TS will not understand that a certain to prop requires the corresponding params. Has anyone got this to work? I've tried different link prop types: LinkOptions, LinkProps, LinkPropsOptions, NavigateOptions, ToOptions
15 Replies
fascinating-indigo
fascinating-indigo2y ago
LinkProps works for me
No description
rare-sapphire
rare-sapphireOP2y ago
Interesting, are you using the latest version? It seems broken in the basic example from the docs as well
rare-sapphire
rare-sapphireOP2y ago
No description
fascinating-indigo
fascinating-indigo2y ago
I'm on 1.1.4. But looking at your screenshot, that's what I have as well. All params are optional, so providing an empty object is fine. I believe that's because it reuses current params if possible. Could be a bug though, not sure of the intention
absent-sapphire
absent-sapphire2y ago
you need to foward the generic type parameters like this:
import {
Link,
AnyRoute,
RegisteredRouter,
RoutePaths,
} from '@tanstack/react-router'

function CustomLink<
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = '',
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom, TMaskTo extends string = ''
>({
linkProps,
children,
}: {
linkProps: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & React.RefAttributes<HTMLAnchorElement>
children: ReactNode
}) {
return <Link {...linkProps}>{children}</Link>
}
import {
Link,
AnyRoute,
RegisteredRouter,
RoutePaths,
} from '@tanstack/react-router'

function CustomLink<
TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
TFrom extends RoutePaths<TRouteTree> | string = string,
TTo extends string = '',
TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom, TMaskTo extends string = ''
>({
linkProps,
children,
}: {
linkProps: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & React.RefAttributes<HTMLAnchorElement>
children: ReactNode
}) {
return <Link {...linkProps}>{children}</Link>
}
rare-sapphire
rare-sapphireOP2y ago
Thanks for the example!
rare-sapphire
rare-sapphireOP2y ago
There seems to be a bug in the router, when pasting the CustomLink in this example https://stackblitz.com/github/tanstack/router/tree/main/examples/react/kitchen-sink?embed=1&theme=dark there's a type error.
StackBlitz
Router Kitchen Sink Example - StackBlitz
Run official live example code for Router Kitchen Sink, created by Tanstack on StackBlitz
rare-sapphire
rare-sapphireOP2y ago
Same in my local environment
helpful-purple
helpful-purple2y ago
this is gold!
quickest-silver
quickest-silver2y ago
if anyone is getting strange typescript errors from this snippet, try upgrading to typescript 5.3.3 :)
helpful-purple
helpful-purple2y ago
Did anything changed inside this approach? It does not seem to give me typings any longer in most recent router update
harsh-harlequin
harsh-harlequin2y ago
export const ForwardLink = forwardRef<HTMLAnchorElement, LinkProps>(
(props, ref) => <Link ref={ref} {...props}></Link>
)
export const ForwardLink = forwardRef<HTMLAnchorElement, LinkProps>(
(props, ref) => <Link ref={ref} {...props}></Link>
)
this works fine for me
metropolitan-bronze
metropolitan-bronze2y ago
I get the same ts error. did you find a solution?
wee-brown
wee-brown14mo ago
wee-brown
wee-brown14mo ago
I changed it a bit so the API is the. same as the original:
const NavigationBarLink = (props: LinkProps & {children: React.ReactNode}) => {;
return (
<li>
<Link
className={cn(
"inline-block whitespace-nowrap border-b py-4 leading-none transition-all sm:px-4"
)}
activeProps={{
className: cn("border-foreground font-semibold"),
}}
inactiveProps={{
className: cn("border-transparent hover:border-foreground/20"),
}}
{...props}
>
{props.children}
</Link>
</li>
);
};
const NavigationBarLink = (props: LinkProps & {children: React.ReactNode}) => {;
return (
<li>
<Link
className={cn(
"inline-block whitespace-nowrap border-b py-4 leading-none transition-all sm:px-4"
)}
activeProps={{
className: cn("border-foreground font-semibold"),
}}
inactiveProps={{
className: cn("border-transparent hover:border-foreground/20"),
}}
{...props}
>
{props.children}
</Link>
</li>
);
};

Did you find this page helpful?