Coder Social home page Coder Social logo

Comments (11)

SeanCassiere avatar SeanCassiere commented on June 3, 2024

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}>

from router.

leqwasd avatar leqwasd commented on June 3, 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; } & {})>'.

from router.

SeanCassiere avatar SeanCassiere commented on June 3, 2024

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

from router.

leqwasd avatar leqwasd commented on June 3, 2024

Adding

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

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

from router.

jfehrman avatar jfehrman commented on June 3, 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.

from router.

SeanCassiere avatar SeanCassiere commented on June 3, 2024

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} />

from router.

schiller-manuel avatar schiller-manuel commented on June 3, 2024

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

from router.

leqwasd avatar leqwasd commented on June 3, 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!

from router.

Maquinours avatar Maquinours commented on June 3, 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'

from router.

ziw avatar ziw commented on June 3, 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.

from router.

jaens avatar jaens commented on June 3, 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} />;
}

from router.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.