Skip to content

Commit f3c6421

Browse files
authored
Merge pull request #9118 from remix-run/pedro/types-for-path-params
refactor(router): simplify types for path param detection
2 parents f1aa2db + f6da1b8 commit f3c6421

File tree

3 files changed

+26
-39
lines changed

3 files changed

+26
-39
lines changed

contributors.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
- morleytatro
5353
- noisypigeon
5454
- paulsmithkc
55+
- pcattori
5556
- petersendidit
5657
- promet99
5758
- RobHannay

docs/utils/generate-path.md

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,6 @@ title: generatePath
88
<summary>Type declaration</summary>
99

1010
```tsx
11-
type PathParams<Path extends string> =
12-
Path extends `:${infer Param}/${infer Rest}`
13-
? Param | PathParams<Rest>
14-
: Path extends `:${infer Param}`
15-
? Param
16-
: Path extends `${any}:${infer Param}`
17-
? PathParams<`:${Param}`>
18-
: Path extends `${any}/*`
19-
? "*"
20-
: Path extends "*"
21-
? "*"
22-
: never;
23-
2411
declare function generatePath<Path extends string>(
2512
path: Path,
2613
params?: {

packages/router/utils.ts

Lines changed: 25 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -161,40 +161,39 @@ export interface DataRouteObject extends RouteObject {
161161
id: string;
162162
}
163163

164-
type Star = "*";
164+
// Recursive helper for finding path parameters in the absence of wildcards
165+
type _PathParam<Path extends string> =
166+
// split path into individual path segments
167+
Path extends `${infer L}/${infer R}` ? _PathParam<L> | _PathParam<R> :
168+
// find params after `:`
169+
Path extends `${string}:${infer Param}` ? Param :
170+
// otherwise, there aren't any params present
171+
never
172+
165173
/**
166-
* @private
167-
* Return string union from path string.
168-
* @example
169-
* PathParam<"/path/:a/:b"> // "a" | "b"
170-
* PathParam<"/path/:a/:b/*"> // "a" | "b" | "*"
174+
* Examples:
175+
* "/a/b/*" -> "*"
176+
* ":a" -> "a"
177+
* "/a/:b" -> "b"
178+
* "/a/blahblahblah:b" -> "b"
179+
* "/:a/:b" -> "a" | "b"
180+
* "/:a/b/:c/*" -> "a" | "c" | "*"
171181
*/
172182
type PathParam<Path extends string> =
173-
// Check path string starts with slash and a param string.
174-
Path extends `:${infer Param}/${infer Rest}`
175-
? Param | PathParam<Rest>
176-
: // Check path string is a param string.
177-
Path extends `:${infer Param}`
178-
? Param
179-
: // Check path string ends with slash and a param string.
180-
Path extends `${any}/:${infer Param}`
181-
? PathParam<`:${Param}`>
182-
: // Check path string ends with slash and a star.
183-
Path extends `${any}/${Star}`
184-
? Star
185-
: // Check string is star.
186-
Path extends Star
187-
? Star
188-
: never;
183+
// check if path is just a wildcard
184+
Path extends "*" ? "*" :
185+
// look for wildcard at the end of the path
186+
Path extends `${infer Rest}/*` ? "*" | _PathParam<Rest> :
187+
// look for params in the absence of wildcards
188+
_PathParam<Path>
189189

190190
// Attempt to parse the given string segment. If it fails, then just return the
191191
// plain string type as a default fallback. Otherwise return the union of the
192192
// parsed string literals that were referenced as dynamic segments in the route.
193-
export type ParamParseKey<Segment extends string> = [
193+
export type ParamParseKey<Segment extends string> =
194+
// if could not find path params, fallback to `string`
195+
[PathParam<Segment>] extends [never] ? string :
194196
PathParam<Segment>
195-
] extends [never]
196-
? PathParam<Segment>
197-
: string;
198197

199198
/**
200199
* The parameters that were parsed from the URL path.

0 commit comments

Comments
 (0)