Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ToOptions doesnt't satisfy <Link> props. #1271

Open
leqwasd opened this issue Mar 7, 2024 · 28 comments
Open

ToOptions doesnt't satisfy <Link> props. #1271

leqwasd opened this issue Mar 7, 2024 · 28 comments

Comments

@leqwasd
Copy link

leqwasd commented Mar 7, 2024

Describe the bug

  1. thing I need - a Component, that wraps , so I can add predefined styles to all of my links. To use "more advanced" paths with them, I found a Type - ToOptions (https://tanstack.com/router/v1/docs/framework/react/api/router/ToOptionsType), that I can pass to Link.
    So I created my own Link component that in its basic form looks like this:
import { Link, ToOptions } from '@tanstack/react-router';
import * as React from 'react';
type MyLinkProps = {
  toOptions: ToOptions;
};
const MyLink: React.FC<React.PropsWithChildren<MyLinkProps>> = ({
  children,
  toOptions,
}) => {
  return <Link {...toOptions}>{children}</Link>;
};
export default MyLink;

It works - I get a nice help from TypeScript autocompletes. It says - that I need "from" property in some situations.

  1. I want routes that looks like this:
    posts/$id
    posts/$id/A
    posts/$id/B

In posts/$id.tsx - I display so I can see the subroutes.

If you run attached project like this - it works, MyLink component is fine.
(See here) https://stackblitz.com/edit/github-rb4ewj?file=src%2FMyLink.tsx

When I navigate to posts/$id - contains nothing... So - I was wondering - and added a new file posts/$id/index.tsx.
(I created a Fork from the previous stackblitz here: https://stackblitz.com/edit/github-rb4ewj-tg9tx2?file=src%2FMyLink.tsx)
And this is where things go wrong - Link now complains about something not satisfying something...

Type '{ children: ReactNode; to: "/" | "/posts/$id" | "/posts/$id/A" | "/posts/$id/B" | "/posts/$id/"; hash?: true | Updater<string> | undefined; state?: true | NonNullableUpdater<HistoryState> | undefined; from?: RoutePathsAutoComplete<...> | undefined; search?: true | ... 1 more ... | undefined; params?: true | ... 1 mo...' is not assignable to type 'IntrinsicAttributes & ({ to?: ToPathOption<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 12 more ..., any>, string, "/" | ... 3 more ... | "/posts/$id/"> | undefined; hash?: true | ... 1 more ... | undefined; state?: true | ... 1 more ... | undefined; from?: Route...'.
  Type '{ children: ReactNode; to: "/" | "/posts/$id" | "/posts/$id/A" | "/posts/$id/B" | "/posts/$id/"; hash?: true | Updater<string> | undefined; state?: true | NonNullableUpdater<HistoryState> | undefined; from?: RoutePathsAutoComplete<...> | undefined; search?: true | ... 1 more ... | undefined; params?: true | ... 1 mo...' is not assignable to type 'MakePathParamOptions<true | ParamsReducer<{} | {} | { id: string; } | ({ id: string; } & {}), {} | {} | ({ id: string; } & {})>>'.
    Types of property 'params' are incompatible.
      Type 'true | ((current: {} | {} | { id: string; } | ({ id: string; } & {})) => never) | undefined' is not assignable to type 'true | ParamsReducer<{} | {} | { id: string; } | ({ id: string; } & {}), {} | {} | ({ id: string; } & {})>'.
        Type 'undefined' is not assignable to type 'true | ParamsReducer<{} | {} | { id: string; } | ({ id: string; } & {}), {} | {} | ({ id: string; } & {})>'.(2322)

All I did - was just create an index.tsx under posts/$id folder.

maybe I am just doing this wrong? How else I can make a default subroute? I do need a common component that wraps children (in this case, it is /src/routes/posts/$id.tsx

Your Example Website or App

https://stackblitz.com/edit/github-rb4ewj?file=src%2FMyLink.tsx

Steps to Reproduce the Bug or Issue

  1. Go here: https://stackblitz.com/edit/github-rb4ewj?file=src%2FMyLink.tsx
  2. Add index.tsx file under /src/routes/posts/$id/ folder (you might neeed to restart vite dev so it regenreates the content for the file)
  3. Open MyLink component and see the issue.

Expected behavior

I expect Link to not complain about ToOptions passed to it.

Screenshots or Videos

No response

Platform

  • OS: ?
  • Browser: ?
    "@tanstack/react-router": "^1.19.0",

Additional context

No response

@SeanCassiere
Copy link
Contributor

ToOptions wouldn't satisfy all the possible options that a <Link> could take.

Try this.

import { LinkProps, Registered router } from "@tanstack/react-router"

interface MyLinkProps {
  linkProps: LinkProps<RegisteredRouter['routeTree']>
}

...
<Link params {...props.linkProps}>

@leqwasd
Copy link
Author

leqwasd commented Mar 7, 2024

  1. LinkProps includes RegisteredRouter by default
export type LinkProps<TRouteTree extends AnyRoute = RegisteredRouter['routeTree'], TFrom ....

And it doesn't work either.

Type '{ children: ((string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | ((state: { ...; }) => ReactNode)) & (string | ... 4 more ... | ReactPortal)) | null | undefined; ... 287 more ...; onTransitionEndCapture?: TransitionEventHandler<...> | undefined; }' is not assignable to type 'IntrinsicAttributes & ({ to?: ToPathOption<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 12 more ..., any>, string, "/" | ... 3 more ... | "/posts/$id/"> | undefined; hash?: true | ... 1 more ... | undefined; state?: true | ... 1 more ... | undefined; from?: Route...'.
  Type '{ children: ((string | number | boolean | ReactElement<any, string | JSXElementConstructor<any>> | Iterable<ReactNode> | ReactPortal | ((state: { ...; }) => ReactNode)) & (string | ... 4 more ... | ReactPortal)) | null | undefined; ... 287 more ...; onTransitionEndCapture?: TransitionEventHandler<...> | undefined; }' is not assignable to type 'MakePathParamOptions<true | ParamsReducer<{} | {} | { id: string; } | ({ id: string; } & {}), {} | {} | ({ id: string; } & {})>>'.
    Types of property 'params' are incompatible.
      Type 'true | ((current: {} | {} | { id: string; } | ({ id: string; } & {})) => never) | undefined' is not assignable to type 'true | ParamsReducer<{} | {} | { id: string; } | ({ id: string; } & {}), {} | {} | ({ id: string; } & {})>'.
        Type 'undefined' is not assignable to type 'true | ParamsReducer<{} | {} | { id: string; } | ({ id: string; } & {}), {} | {} | ({ id: string; } & {})>'.

@SeanCassiere
Copy link
Contributor

@schiller-manuel did anything change here? LinkProps used to work just fine.

@leqwasd
Copy link
Author

leqwasd commented Mar 11, 2024

Adding

{...toOptions}
from={"/"}

satisfies props.. But this will overwrite the "from" from the toOptions

@jfehrman
Copy link

jfehrman commented Mar 13, 2024

Not sure if it covers all your use-cases, but I was able to get a Link wrapper working with something like this. Personally I find it cleaner passing the whole route object as opposed to just the to. That also allowed me to get by without having to pass a generic type to my custom Link component since typescript can directly infer it from the route prop.

// helper types
type AtLeastOne<T, U = { [K in keyof T]: Pick<T, K> }> = Partial<T> & U[keyof U];
type ExcludeEmpty<T> = T extends AtLeastOne<T> ? T : never;

// seem to be as safe/strict as the props of the tanstack-provided Link component
type RouteParams<T extends AnyRoute = RegisteredRouter['routeTree']> = ExcludeEmpty<
  T['types']['params']
> extends never
  ? { params?: never }
  : { params: T['types']['params'] };
type RouteSearch<T extends AnyRoute = RegisteredRouter['routeTree']> = ExcludeEmpty<
  T['types']['searchSchema']
> extends never
  ? { search?: never }
  : {
      search?: T['types']['searchSchema'] | ((args: T['types']['fullSearchSchema']) => T['types']['searchSchema']);
    };

// component props
type MyLinkProps<T extends AnyRoute = RegisteredRouter['routeTree']> = {
  route: T;
  children: React.ReactNode;
  className?: string;
  disabled?: boolean;
} & RouteParams<T> &
  RouteSearch<T>;

// custom Link component
function MyLink<T extends AnyRoute = RegisteredRouter['routeTree']>({
  route,
  children,
  ...other
}: MyLinkProps<T>) {
  return (
    <Link<AnyRoute> to={route?.to} {...other}>
      {children}
    </Link>
  );
}

As a note - there might be a better way to skin this cat, but the docs seem to be pretty limited on the topic. Just figured I would share what worked for me in case you were still stuck here.

@SeanCassiere
Copy link
Contributor

So turns out this is what works.

const MyLink = <
  TRouteTree extends AnyRoute = RegisteredRouter['routeTree'],
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = '',
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
  TMaskTo extends string = '',
>(
  props: React.PropsWithoutRef<
    LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
      Omit<React.HTMLProps<'a'>, 'children' | 'preload'>
  >
) => <Link {...props} />

@schiller-manuel
Copy link
Contributor

can we make use of this in createLink?

I don't want to expose all of these generics as public API, otherwise we cannot modify them without causing breaking changes

@leqwasd
Copy link
Author

leqwasd commented Mar 19, 2024

So did anyone inspected - what's wrong with ToOptions? In my optinion - that is a user friendly type to be used in this place!

@Maquinours
Copy link

Maquinours commented Mar 19, 2024

This code was working

type MenuItem = { label: string; route: LinkProps; };
const MENUS: MenuItem[] = [ { label: 'Test', route: { params: (old) => old, search: (old) => ({...old, test: true}), }, }];

But I updated from 1.16.6 to 1.20.1 and it does not work anymore. I got the error :
Type '{ params: (old: never) => never; search: (old: {}) => { test: boolean }; }' is not assignable to type 'LinkProps'. Property 'to' is missing in type '{ params: (old: never) => never; search: (old: {}) => { test: boolean }; }' but required in type 'CheckPathError<Route<any, "/", "/", string, "__root__", RootSearchSchema, RootSearchSchema, RootSearchSchema, RootSearchSchema, ... 11 more ..., any>>'.ts(2322) link.d.ts(72, 5): 'to' is declared here. Test.tsx(14, 3): The expected type comes from property 'route' which is declared here on type 'MenuItem'

@ziw
Copy link

ziw commented Mar 24, 2024

I've seen many similar issues/discussions and stackoverflow posts. At this point, we could really use some official document / examples on how to wrap components in a type safe way. Even the customized solutions that work, only the to prop is typed but things like search and params do not get type safety that match to.

@jaens
Copy link
Contributor

jaens commented Mar 24, 2024

Yeah, this is basically the same as #1194. I proposed contributing a solution but there was no response from the maintainers at that time.

Here's the workaround I use currently:

(It uses a wrapper function to create the link options, so it can be used as a regular object. This also works for passing it into a component without requiring a ton of boilerplate generics everywhere.)

export type RouterLinkProps = Parameters<RegisteredRouter["navigate"]>[0];

/**
 * Validate a router link as type-safe and return a generic {@link RouterLinkProps}.
 * @example link({ to: "/view/$id", params: { id: "1" } })
 */
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 RouterLinkProps;
}

The type can also be spread directly:

const someLink = link({ .... });
...
function MyComponent({ someLink } : { someLink: RouterLinkProps }) {
	return <Link {...someLink} />;
}

@Jarzka
Copy link

Jarzka commented Mar 25, 2024

I tried to update my typings based on @jaens example, but unfortunately it does not work in 1.22.0. My version looks like this:

function MyLink<
  TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = "",
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
  TMaskTo extends string = "",
>({
  variant,
  testId,
  linkProps,
  children,
}: {
  variant?: StyledLinkProps["variant"];
  testId?: StyledLinkProps["testId"];
  linkProps: UseLinkPropsOptions<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo>;
  children?: ReactNode;
}) {
  return (
    <StyledLink variant={variant} testId={testId}>
      <Link {...linkProps}>{children}</Link>
    </StyledLink>
  );
}

I also tried this version by @SeanCassiere. This was interesting because my editor is happy but npm run build still breaks:

function MyLink<
  TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = "",
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
  TMaskTo extends string = "",
>({
  variant,
  testId,
  linkProps,
  children,
}: {
  variant?: StyledLinkProps["variant"];
  testId?: StyledLinkProps["testId"];
  linkProps: React.PropsWithoutRef<
    LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
      Omit<React.HTMLProps<'a'>, 'children' | 'preload'>
  >;
  children?: ReactNode;
}) {
  return (
    <StyledLink variant={variant} testId={testId}>
      <Link {...linkProps}>{children}</Link>
    </StyledLink>
  );
}

This previous version worked fine in 1.19.4:

function MyLink<
  TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = "",
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
  TMaskTo extends string = "",
>({
  variant,
  testId,
  linkProps,
  children,
}: {
  variant?: StyledLinkProps["variant"];
  testId?: StyledLinkProps["testId"];
  linkProps: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> & React.RefAttributes<HTMLAnchorElement>;
  children?: ReactNode;
}) {
  return (
    <StyledLink variant={variant} testId={testId}>
      <Link {...linkProps}>{children}</Link>
    </StyledLink>
  );
}

I've seen many similar issues/discussions and stackoverflow posts. At this point, we could really use some official document / examples on how to wrap components in a type safe way

@ziw 100% this. There is a clear need to create a custom wrapper for the provided Link component and it becomes a big problem if the typings of the library do not remain stable.

@SeanCassiere
Copy link
Contributor

I also tried this version by @SeanCassiere. This was interesting because my editor is happy but npm run build still breaks:

Not sure if this helps, but there's a variation of this, that I'm using that works.

const AppNavigationLink = <
  TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = "",
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
  TMaskTo extends string = "",
>(props: {
  name: string;
  props: Omit<
    React.PropsWithoutRef<
      LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
        Omit<React.ComponentPropsWithoutRef<"a">, "preload">
    >,
    "children" | "className" | "activeProps" | "inactiveProps"
  >;
}) => {
  const { name, props: linkProps } = props;
  return (
    <li>
      <Link
        className="..."
        activeProps={{ className: "..." }}
        inactiveProps={{ className: "..." }}
        {...linkProps}
      >
        {name}
      </Link>
    </li>
  );
};

Source

@SeanCassiere
Copy link
Contributor

I've seen many similar issues/discussions and stackoverflow posts. At this point, we could really use some official document / examples on how to wrap components in a type safe way

@ziw 100% this. There is a clear need to create a custom wrapper for the provided Link component and it becomes a big problem if the typings of the library do not remain stable.

We are working on this!

From what I can gather, most of the questions on the Discord questions channel and here related to this topic, is around having a type which can be applied into a custom component's props, to then later be applied on the TSR provided <Link> component.

import { Link, type SomeNewLinkPropsType } from "@tanstack/react-router";

function CustomLink({ linkProps } : { linkProps: SomeNewLinkPropsType }) {
  const { className, ...rest } = linkProps;
  return <Link className={`foo-bar ${className}`} {...rest} />
}

Once we figure out the correct story for the user-facing types for this, we'll make sure the documentation is updated to reflect the correct way forward.

@TkDodo
Copy link
Contributor

TkDodo commented Mar 26, 2024

So I looked at createLink, and I think it works pretty much the way we want it to, regarding type-safety and everything. Here's a TS playground:

https://tsplay.dev/Wzq93m

I've tested with additional properties, searchParams validation etc, it all looks good. One issue is that createLink freezes the browser at runtime. No kidding, this was weird, but I have a PR open that fixes it.

Once that is merged, I think createLink is a good abstraction to use in user-land, because it's type-safe and you can compose over every component that you want.

Only requirement is that props are being spread onto an actual a tag. This is something that is not type-safe, and maybe something that we should document.

Let me know if there's anything I'm missing

@lecstor
Copy link
Contributor

lecstor commented Mar 28, 2024

So I looked at createLink, and I think it works pretty much the way we want it to, regarding type-safety and everything. Here's a TS playground:

https://tsplay.dev/Wzq93m

ok, cool, that seems to mostly work..

One issue though is that once the component is wrapped in createLink it has some props which are set to undefined or an empty object. When deconstructing these into the component they overwrite any props set by the component (as intended, if the props were actually set by the consumer).

eg the className set in the example below is overwritten with undefined from the props.

const MyStyledRouterLink = createLink(MyStyledLink);

function MyStyledLink({ children, ...props }: React.ComponentProps<"a">) {
  return (
    <a className="foo-bar" {...props}>
      {children}
    </a>
  );
}

if I stringify & parse the props to remove the undefined it works as intended.

{...JSON.parse(JSON.stringify(props))}
Screenshot 2024-03-28 at 11 01 16 pm

@lecstor
Copy link
Contributor

lecstor commented Mar 28, 2024

also.. I was pleasantly surprised when I initially used Link for external links and it all seemed to work fine. While testing createLink I noticed that it complained about the absolute url not being assignable, then I tested Link and it did the same so I thought I must have been tripping, but looking at the code it looks like it's intended to work, so I include the props I see for those as well..

Screenshot 2024-03-28 at 10 58 02 pm

and submit a PR which I believe resolves the props issue.. (but not the type error on to with absolute urls)
#1386

@vixducis
Copy link
Contributor

vixducis commented Apr 4, 2024

It's probably not relevant anymore, but LinkProps broke for me in 1.17.5. It still worked in 1.17.4.

@dohomi
Copy link

dohomi commented May 14, 2024

I am using 1.32.5 and the custom component linkProps does throw some TS issues:

Type TRouteTree does not satisfy the constraint AnyRouter

I think this is related to the same issue, I had following code which does not satisfy TS any longer:

function TabbedNavigationItem<
  TRouteTree extends AnyRoute = RegisteredRouter["routeTree"],
  TFrom extends RoutePaths<TRouteTree> | string = string,
  TTo extends string = "",
  TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,
  TMaskTo extends string = ""
>({
  label,
  activeHref,
  linkProps
}: {
  linkProps: LinkProps<TRouteTree, TFrom, TTo, TMaskFrom, TMaskTo> &
    Omit<React.HTMLProps<"a">, "children" | "preload">
  label: string
  activeHref: LinkProps["to"]
}) {

@enaluz
Copy link

enaluz commented May 19, 2024

The weird TS issues I was having seems to be resolved in version 1.33.4. This worked for me, with the exception of having to add a ts-expect-error on the actual <Link/> component. Unsure exactly how to resolve it, but usage of this component works in practice.

// ...omitted
import type { LinkProps, RegisteredRouter, RoutePaths } from '@tanstack/react-router';
import { Link } from '@tanstack/react-router';

export type LinkButtonProps<
  TRouter extends RegisteredRouter = RegisteredRouter,
  TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
  TTo extends string = '',
  TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
  TMaskTo extends string = '',
> = Pick<LinkProps<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>, 'params' | 'search' | 'to'> &
  StrictOmit<ButtonProps, 'onClick'>;

export const LinkButton = <
  TRouter extends RegisteredRouter = RegisteredRouter,
  TFrom extends RoutePaths<TRouter['routeTree']> | string = string,
  TTo extends string = '',
  TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom,
  TMaskTo extends string = '',
>({
  params,
  search,
  to,
  ...props
}: LinkButtonProps<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>) => {
  return (
    // @ts-expect-error Not sure what's causing this type error
    <Link params={params} search={search} to={to}>
      <Button {...props} />
    </Link>
  );
};

In practice:

// No type error
<LinkButton params={{ productId: "123" }} to="/app/product/$productId" label="Go to Product"   />

// Type error (expects params prop)
<LinkButton to="/app/product/$productId" label="Go to Product"   />

@Jarzka
Copy link

Jarzka commented May 22, 2024

Since creating a custom wrapper component for Link with working typing seems to be a) difficult, b) easy to break with version updates and c) not officially supported (not mentioned in the documentation), we decided to create our links using the official Link component, but by wrapping it with our own StyledLink component, which adds styling. It looks like this:

function StyledLink({ children, testId }: StyledLinkProps) {
  const childrenWithClassName = Children.map(children, (child) =>
    cloneElement(child, {
      className: classes.styledLink,
      "data-testid": testId,
    }),
  );

  return <>{childrenWithClassName}</>;
}

We use it like this:

<StyledLink testId="myAnchor">
  <Link
    to={routeToSeppo}
    params={seppoParams}
  >
    Seppo Taalasmaa
  </Link>
</StyledLink>

One downside with this approach is that we need to remember to manually wrap every Link with StyledLink. Still, I hope this solution is more stable since we do not have any custom typing logic.

@schiller-manuel
Copy link
Contributor

@Jarzka did you try out createLink?
we are still gathering feedback about this before we promote it from experimental

@TkDodo
Copy link
Contributor

TkDodo commented May 22, 2024

didn't know that createLink is experimental but really, it works great on type level and on runtime (since I fixed it 😂). Also no type parameters needed in user-land. All those solutions with 5 type parameters are way too complicated for app level code.

Here's what I do:

import { Button } from 'my-design-system'

const ButtonWrapper = (props: Omit<React.ComponentProps<typeof Button>, 'as'>) => <Button as="a" {...props} />

export const RouterButton = createLink(ButtonWrapper)

that's it 🤷 . Now I can do:

<RouterButton to="/my/route/" params={{ this: 'is', typesafe: true }}>Hello</RouterButton>

everything is styled like my normal <Button as="a"> and we get type-safe to navigation. What's not to love :)

@tobyzerner
Copy link

tobyzerner commented May 23, 2024

@TkDodo how do you apply activeProps without having to pass them every time?

I want to be able to achieve something like this:

import { ListItem } from 'my-design-system';

function NavListItem(props: ButtonProps & LinkProps) {
    return <ListItem as={Link} {...props} activeProps={{ className: 'ListItem-active' }} />;
}

<NavListItem to="/my/route" params={{ foo: 'bar' }}>
  My Link
</NavListItem>

@Jarzka
Copy link

Jarzka commented May 23, 2024

@Jarzka did you try out createLink? we are still gathering feedback about this before we promote it from experimental

Yes, I am aware of createLink, but we did not want to use it for a few reasons:

  • We are slowly heading towards production and don't want to use experimental / undocumented features at this point
  • I have bad memories of the removal of buildLink during the beta period as it caused us to do massive refactoring for almost all links.
  • We want to maintain the possibility to switch the library in case of emergency, so we do not want to rely too much on TanStack specific features.

@TkDodo
Copy link
Contributor

TkDodo commented May 23, 2024

@TkDodo how do you apply activeProps without having to pass them every time?

Haven't done this yet since I only want this passed for our sideBar navigation, where I pass it manually. But you should be able to do one more layer of nesting:

import { ListItem } from 'my-design-system'

const ListItemWrapper = (props: Omit<React.ComponentProps<typeof ListItem>, 'as'>) => <ListItem as={Link} {...props} />

const CreatedNavListItem = createLink(ListItemWrapper)

export const NavListItem: LinkComponent<typeof ListItemWrapper> = (props) => (
  return <CreatedNavListItem {...props} activeProps={{ className: 'ListItem-active' }} />
)

@leqwasd
Copy link
Author

leqwasd commented May 23, 2024

And what about ToOptions type? What is the purpose of it? Where is it suppose to be used? Maybe it shouldn't even be exported

@SeanCassiere
Copy link
Contributor

And what about ToOptions type? What is the purpose of it? Where is it suppose to be used? Maybe it shouldn't even be exported

The ToOptions type mostly comes in handy when you need access to the inference of the Router's available routes. For custom functions, etc... It's delicate since it needs access to generics to work correctly, and it's something that can certainly change as we push for better ts-performance.

It's not a magic bullet type for use with <Link> or useNavigate(), rather it's for more fine-grained types.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests