@@ -161,51 +161,40 @@ export interface DataRouteObject extends RouteObject {
161
161
id : string ;
162
162
}
163
163
164
- type ParamParseFailed = { failed : true } ;
165
-
166
- type ParamParseSegment < Segment extends string > =
167
- // Check here if there exists a forward slash in the string.
168
- Segment extends `${infer LeftSegment } /${infer RightSegment } `
169
- ? // If there is a forward slash, then attempt to parse each side of the
170
- // forward slash.
171
- ParamParseSegment < LeftSegment > extends infer LeftResult
172
- ? ParamParseSegment < RightSegment > extends infer RightResult
173
- ? LeftResult extends string
174
- ? // If the left side is successfully parsed as a param, then check if
175
- // the right side can be successfully parsed as well. If both sides
176
- // can be parsed, then the result is a union of the two sides
177
- // (read: "foo" | "bar").
178
- RightResult extends string
179
- ? LeftResult | RightResult
180
- : LeftResult
181
- : // If the left side is not successfully parsed as a param, then check
182
- // if only the right side can be successfully parse as a param. If it
183
- // can, then the result is just right, else it's a failure.
184
- RightResult extends string
185
- ? RightResult
186
- : ParamParseFailed
187
- : ParamParseFailed
188
- : // If the left side didn't parse into a param, then just check the right
189
- // side.
190
- ParamParseSegment < RightSegment > extends infer RightResult
191
- ? RightResult extends string
192
- ? RightResult
193
- : ParamParseFailed
194
- : ParamParseFailed
195
- : // If there's no forward slash, then check if this segment starts with a
196
- // colon. If it does, then this is a dynamic segment, so the result is
197
- // just the remainder of the string, optionally prefixed with another string.
198
- // Otherwise, it's a failure.
199
- Segment extends `${string } :${infer Remaining } `
200
- ? Remaining
201
- : ParamParseFailed ;
164
+ type Star = "*"
165
+ /**
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" | "*"
171
+ */
172
+ type PathParam <
173
+ Path extends string
174
+ > =
175
+ // Check path string starts with slash and a param string.
176
+ Path extends `:${infer Param } /${infer Rest } `
177
+ ? Param | PathParam < Rest >
178
+ // Check path string is a param string.
179
+ : Path extends `:${infer Param } `
180
+ ? Param
181
+ // Check path string ends with slash and a param string.
182
+ : Path extends `${any } /:${infer Param } `
183
+ ? PathParam < `:${Param } `>
184
+ // Check path string ends with slash and a star.
185
+ : Path extends `${any } /${Star } `
186
+ ? Star
187
+ // Check string is star.
188
+ : Path extends Star
189
+ ? Star
190
+ : never
202
191
203
192
// Attempt to parse the given string segment. If it fails, then just return the
204
193
// plain string type as a default fallback. Otherwise return the union of the
205
194
// parsed string literals that were referenced as dynamic segments in the route.
206
195
export type ParamParseKey < Segment extends string > =
207
- ParamParseSegment < Segment > extends string
208
- ? ParamParseSegment < Segment >
196
+ [ PathParam < Segment > ] extends [ never ]
197
+ ? PathParam < Segment >
209
198
: string ;
210
199
211
200
/**
@@ -475,14 +464,20 @@ function matchRouteBranch<
475
464
*
476
465
* @see https://reactrouter.com/docs/en/v6/utils/generate-path
477
466
*/
478
- export function generatePath ( path : string , params : Params = { } ) : string {
467
+ export function generatePath < Path extends string > ( path : Path , params : {
468
+ [ key in PathParam < Path > ] : string
469
+ } = { } as any ) : string {
479
470
return path
480
- . replace ( / : ( \w + ) / g, ( _ , key ) => {
471
+ . replace ( / : ( \w + ) / g, ( _ , key : PathParam < Path > ) => {
481
472
invariant ( params [ key ] != null , `Missing ":${ key } " param` ) ;
482
473
return params [ key ] ! ;
483
474
} )
484
475
. replace ( / \/ * \* $ / , ( _ ) =>
485
- params [ "*" ] == null ? "" : params [ "*" ] . replace ( / ^ \/ * / , "/" )
476
+ {
477
+ const star = "*" as PathParam < Path >
478
+
479
+ return params [ star ] == null ? "" : params [ star ] . replace ( / ^ \/ * / , "/" )
480
+ }
486
481
) ;
487
482
}
488
483
0 commit comments