Skip to content

Commit 4a1e63f

Browse files
fix: parse params union inference (#7306)
1 parent e31f7aa commit 4a1e63f

3 files changed

Lines changed: 75 additions & 7 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@tanstack/router-core': patch
3+
---
4+
5+
Fix params.parse inference for discriminated union path params while preserving path key validation.

packages/react-router/tests/fileRoute.test-d.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,31 @@ const postRoute = createFileRoute('/_postLayout/posts/$postId_')()
1717

1818
const protectedRoute = createFileRoute('/(auth)/protected')()
1919

20+
const attachmentRoute = createFileRoute(
21+
'/projects/$observationDocId/attachments/$driveId/$type/$variant/$name',
22+
)({
23+
params: {
24+
parse: ({ driveId, type, variant, name, ...rest }) => {
25+
const blobId =
26+
type === 'audio'
27+
? {
28+
type: 'audio' as const,
29+
variant: 'original' as const,
30+
driveId,
31+
name,
32+
}
33+
: {
34+
type: 'photo' as const,
35+
variant: variant as 'original' | 'preview' | 'thumbnail',
36+
driveId,
37+
name,
38+
}
39+
40+
return { ...rest, ...blobId }
41+
},
42+
},
43+
})
44+
2045
declare module '@tanstack/router-core' {
2146
interface FileRoutesByPath {
2247
'/': {
@@ -68,6 +93,13 @@ declare module '@tanstack/router-core' {
6893
fullPath: '/posts/$postId'
6994
path: '/$postId_'
7095
}
96+
'/projects/$observationDocId/attachments/$driveId/$type/$variant/$name': {
97+
preLoaderRoute: typeof attachmentRoute
98+
parentRoute: typeof rootRoute
99+
id: '/projects/$observationDocId/attachments/$driveId/$type/$variant/$name'
100+
fullPath: '/projects/$observationDocId/attachments/$driveId/$type/$variant/$name'
101+
path: '/projects/$observationDocId/attachments/$driveId/$type/$variant/$name'
102+
}
71103
}
72104
}
73105

@@ -100,3 +132,31 @@ test('when creating a folder group', () => {
100132
expectTypeOf<'(auth)/protected'>(protectedRoute.path)
101133
expectTypeOf<'/protected'>(protectedRoute.id)
102134
})
135+
136+
test('when file route params.parse returns a discriminated union', () => {
137+
expectTypeOf(attachmentRoute.types.params).toEqualTypeOf<
138+
| {
139+
observationDocId: string
140+
driveId: string
141+
type: 'audio'
142+
variant: 'original'
143+
name: string
144+
}
145+
| {
146+
observationDocId: string
147+
driveId: string
148+
type: 'photo'
149+
variant: 'original' | 'preview' | 'thumbnail'
150+
name: string
151+
}
152+
>()
153+
})
154+
155+
test('when file route params.parse drops a path param', () => {
156+
createFileRoute('/invoices/$invoiceId')({
157+
params: {
158+
// @ts-expect-error parsed params must keep path param keys
159+
parse: () => ({}),
160+
},
161+
})
162+
})

packages/router-core/src/route.ts

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -173,26 +173,29 @@ export type ResolveParams<
173173

174174
export type ParseParamsFn<in out TPath extends string, in out TParams> = (
175175
rawParams: Expand<ResolveParams<TPath>>,
176-
) =>
177-
| (TParams extends ResolveParams<TPath, any>
178-
? TParams
179-
: ResolveParams<TPath, any>)
180-
| false
176+
) => TParams | false
177+
178+
type ValidateParsedParams<TPath extends string, TParams> = [TParams] extends [
179+
ResolveParams<TPath, any>,
180+
]
181+
? unknown
182+
: never
181183

182184
export type StringifyParamsFn<in out TPath extends string, in out TParams> = (
183185
params: TParams,
184186
) => ResolveParams<TPath>
185187

186188
export type ParamsOptions<in out TPath extends string, in out TParams> = {
187189
params?: {
188-
parse?: ParseParamsFn<TPath, TParams>
190+
parse?: ParseParamsFn<TPath, TParams> & ValidateParsedParams<TPath, TParams>
189191
stringify?: StringifyParamsFn<TPath, TParams>
190192
}
191193

192194
/**
193195
@deprecated Use params.parse instead
194196
*/
195-
parseParams?: ParseParamsFn<TPath, TParams>
197+
parseParams?: ParseParamsFn<TPath, TParams> &
198+
ValidateParsedParams<TPath, TParams>
196199

197200
/**
198201
@deprecated Use params.stringify instead

0 commit comments

Comments
 (0)