Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion packages/router-core/src/typePrimitives.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,11 @@ import type { AnyRouter, RegisteredRouter } from './router'
import type { UseParamsResult } from './useParams'
import type { UseSearchResult } from './useSearch'
import type { Constrain, ConstrainLiteral } from './utils'
// Remove any `_pathless` folder segments for type extraction
export type StripPathless<T extends string> =
T extends `${infer A}/_pathless/${infer B}`
? `${A}/${B}`
: T;
Comment on lines +14 to +18
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

StripPathless only removes the first _pathless segment; consider making it recursive

As written, StripPathless stops after stripping the first /_pathless/ occurrence, so paths with multiple _pathless folders (e.g. "/foo/_pathless/bar/_pathless/baz") will still contain a _pathless segment and won’t be fully normalized for param extraction.

You can make this handle all occurrences with a small recursive change:

-export type StripPathless<T extends string> =
-  T extends `${infer A}/_pathless/${infer B}`
-    ? `${A}/${B}`
-    : T;
+export type StripPathless<T extends string> =
+  T extends `${infer A}/_pathless/${infer B}`
+    ? StripPathless<`${A}/${B}`>
+    : T;

This preserves behavior for simple cases like "/_pathless/nested/$id" while correctly normalizing any deeper _pathless nesting. Based on learnings, this keeps the special handling scoped to the _pathless folder name without affecting other underscore semantics.

🤖 Prompt for AI Agents
In packages/router-core/src/typePrimitives.ts around lines 14 to 18,
StripPathless currently only removes the first "/_pathless/" segment; change it
to be recursive so all occurrences are stripped. Replace the conditional branch
so that when T extends `${infer A}/_pathless/${infer B}` you return
StripPathless<`${A}/${B}`> (rather than just `${A}/${B}`) so nested/multiple
_pathless segments are normalized; keep the fallback as T.


export type ValidateFromPath<
TRouter extends AnyRouter = RegisteredRouter,
Expand All @@ -29,11 +34,16 @@ export type ValidateSearch<
TFrom extends string = string,
> = SearchParamOptions<TRouter, TFrom, TTo>


export type ValidateParams<
TRouter extends AnyRouter = RegisteredRouter,
TTo extends string | undefined = undefined,
TFrom extends string = string,
> = PathParamOptions<TRouter, TFrom, TTo>
> = PathParamOptions<
TRouter,
StripPathless<TFrom>,
TTo extends string ? StripPathless<TTo> : TTo
>

/**
* @private
Expand Down