Skip to content

fix(react-router): fix custom link type ref inference #4121

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

CynicalBusiness
Copy link

@CynicalBusiness CynicalBusiness commented May 9, 2025

Custom links created by createLink would incorrectly infer ref values for non-intrinsic components (such as those with their own ref prop like the documented example), which would result in the ref being "double-wrapped". That is:

Before:

{
  // Effective LinkComponentReactProps<TComp>
  // ...
  ref?: React.Ref<React.Ref<HTMLAnchorElement> | undefined> | undefined
  // notice the ref has been "ref'ed" twice
}

After fix:

{
  // Effective LinkComponentReactProps<TComp>
  // ...
  ref?: React.Ref<HTMLAnchorElement> | undefined>
}

This patch, the equivilant applied directly to link.d.ts via yarn patch, has been our workaround and it appears to work well:

@tanstack-react-router-npm-1.120.3-f6c72d7c75.patch
diff --git a/dist/esm/link.d.ts b/dist/esm/link.d.ts
index f7a4dfc2678f3669ee48b97c22e4c69d2f3ff195..be0adda3bf4302466c3c4b4d73ff6c78474a237f 100644
--- a/dist/esm/link.d.ts
+++ b/dist/esm/link.d.ts
@@ -4,9 +4,9 @@ import { ValidateLinkOptions, ValidateLinkOptionsArray } from './typePrimitives.
 import * as React from 'react';
 export declare function useLinkProps<TRouter extends AnyRouter = RegisteredRouter, const TFrom extends string = string, const TTo extends string | undefined = undefined, const TMaskFrom extends string = TFrom, const TMaskTo extends string = ''>(options: UseLinkPropsOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo>, forwardedRef?: React.ForwardedRef<Element>): React.ComponentPropsWithRef<'a'>;
 type UseLinkReactProps<TComp> = TComp extends keyof React.JSX.IntrinsicElements ? React.JSX.IntrinsicElements[TComp] : React.PropsWithoutRef<TComp extends React.ComponentType<infer TProps> ? TProps : never> & React.RefAttributes<TComp extends React.FC<{
-    ref: infer TRef;
+    ref: React.Ref<infer TRef>;
 }> | React.Component<{
-    ref: infer TRef;
+    ref: React.Ref<infer TRef>;
 }> ? TRef : never>;
 export type UseLinkPropsOptions<TRouter extends AnyRouter = RegisteredRouter, TFrom extends RoutePaths<TRouter['routeTree']> | string = string, TTo extends string | undefined = '.', TMaskFrom extends RoutePaths<TRouter['routeTree']> | string = TFrom, TMaskTo extends string = '.'> = ActiveLinkOptions<'a', TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & UseLinkReactProps<'a'>;
 export type ActiveLinkOptions<TComp = 'a', TRouter extends AnyRouter = RegisteredRouter, TFrom extends string = string, TTo extends string | undefined = '.', TMaskFrom extends string = TFrom, TMaskTo extends string = '.'> = LinkOptions<TRouter, TFrom, TTo, TMaskFrom, TMaskTo> & ActiveLinkOptionProps<TComp>;

@schiller-manuel
Copy link
Contributor

can you add a type test for this?

Copy link

nx-cloud bot commented May 12, 2025

View your CI Pipeline Execution ↗ for commit 95ff452.

Command Status Duration Result
nx affected --targets=test:eslint,test:unit,tes... ❌ Failed 4m 11s View ↗
nx run-many --target=build --exclude=examples/*... ✅ Succeeded 1m 11s View ↗

☁️ Nx Cloud last updated this comment at 2025-05-12 17:37:24 UTC

Copy link

pkg-pr-new bot commented May 12, 2025

More templates

@tanstack/arktype-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/arktype-adapter@4121

@tanstack/directive-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/directive-functions-plugin@4121

@tanstack/eslint-plugin-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/eslint-plugin-router@4121

@tanstack/history

npm i https://pkg.pr.new/TanStack/router/@tanstack/history@4121

@tanstack/react-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router@4121

@tanstack/react-router-with-query

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-with-query@4121

@tanstack/react-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-router-devtools@4121

@tanstack/react-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start@4121

@tanstack/react-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-client@4121

@tanstack/react-start-config

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-config@4121

@tanstack/react-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-plugin@4121

@tanstack/react-start-router-manifest

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-router-manifest@4121

@tanstack/react-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/react-start-server@4121

@tanstack/router-cli

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-cli@4121

@tanstack/router-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-core@4121

@tanstack/router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools@4121

@tanstack/router-devtools-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-devtools-core@4121

@tanstack/router-generator

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-generator@4121

@tanstack/router-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-plugin@4121

@tanstack/router-utils

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-utils@4121

@tanstack/router-vite-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/router-vite-plugin@4121

@tanstack/server-functions-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/server-functions-plugin@4121

@tanstack/solid-router

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router@4121

@tanstack/solid-router-devtools

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-router-devtools@4121

@tanstack/solid-start

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start@4121

@tanstack/solid-start-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-client@4121

@tanstack/solid-start-config

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-config@4121

@tanstack/solid-start-plugin

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-plugin@4121

@tanstack/solid-start-router-manifest

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-router-manifest@4121

@tanstack/solid-start-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/solid-start-server@4121

@tanstack/start

npm i https://pkg.pr.new/TanStack/router/@tanstack/start@4121

@tanstack/start-api-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-api-routes@4121

@tanstack/start-client-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-client-core@4121

@tanstack/start-config

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-config@4121

@tanstack/start-server-core

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-core@4121

@tanstack/start-server-functions-client

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-client@4121

@tanstack/start-server-functions-fetcher

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-fetcher@4121

@tanstack/start-server-functions-handler

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-handler@4121

@tanstack/start-server-functions-server

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-server@4121

@tanstack/start-server-functions-ssr

npm i https://pkg.pr.new/TanStack/router/@tanstack/start-server-functions-ssr@4121

@tanstack/valibot-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/valibot-adapter@4121

@tanstack/virtual-file-routes

npm i https://pkg.pr.new/TanStack/router/@tanstack/virtual-file-routes@4121

@tanstack/zod-adapter

npm i https://pkg.pr.new/TanStack/router/@tanstack/zod-adapter@4121

commit: 95ff452

@CynicalBusiness
Copy link
Author

CynicalBusiness commented May 14, 2025

can you add a type test for this?

Where would make the most sense?
My assumption would be in link.test-d.tsx but createLink is only ever really tested the once and not very thoroughly. Would a whole new test block be preferred or would expanding the linked line be enough?

@schiller-manuel
Copy link
Contributor

link.test-d.tsx sounds reasonable. feel free to add test blocks as makes sense to you

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

Successfully merging this pull request may close these issues.

2 participants