-
-
Notifications
You must be signed in to change notification settings - Fork 10.8k
Description
Reproduction
https://stackblitz.com/edit/github-fguzcsck?file=app%2Froutes%2Fhome.tsx
Note that you may need to click in the editor and add then remove a space to trigger a type-checking pass.
System Info
System:
OS: Linux 5.0 undefined
CPU: (8) x64 Intel(R) Core(TM) i9-9880H CPU @ 2.30GHz
Memory: 0 Bytes / 0 Bytes
Shell: 1.0 - /bin/jsh
Binaries:
Node: 20.19.1 - /usr/local/bin/node
Yarn: 1.22.19 - /usr/local/bin/yarn
npm: 10.8.2 - /usr/local/bin/npm
pnpm: 8.15.6 - /usr/local/bin/pnpm
npmPackages:
@react-router/dev: ^7.5.3 => 7.6.3
@react-router/node: ^7.5.3 => 7.6.3
@react-router/serve: ^7.5.3 => 7.6.3
react-router: ^7.5.3 => 7.6.3
vite: ^6.3.3 => 6.3.5Used Package Manager
npm
Expected Behavior
As shown as the reproduction, the current runtime behavior of generatePath is as follows:
generatePath("/users/:id?", { id: "123" })returns/users/123.generatePath("/users/:id?", { id: null })returns/users.generatePath("/users/:id?", { id: undefined })returns/users.generatePath("/users/:id?", {})returns/users.
Because generatePath uses == null and != null checks in its implementation, it accepts either null or undefined for optional parameters.
react-router/packages/react-router/lib/router/utils.ts
Lines 1091 to 1092 in 0dea762
| const stringify = (p: any) => | |
| p == null ? "" : typeof p === "string" ? p : String(p); |
| invariant(optional === "?" || param != null, `Missing ":${key}" param`); |
I would expect that its type signature would similarly accommodate all these options.
Actual Behavior
The params argument in generatePath currently has the type params: { [key in PathParam<Path>]: string | null; }, meaning it requires missing parameters to be specified as null, and doesn't allow them to be specified as undefined or omitted entirely.
react-router/packages/react-router/lib/router/utils.ts
Lines 1072 to 1074 in 0dea762
| params: { | |
| [key in PathParam<Path>]: string | null; | |
| } = {} as any |
This is problematic because it rules out some of the usages that yield valid runtime behavior.
The expression generatePath("/users/:id?", { id: undefined }) currently gives the following type error:
The expression generatePath("/users/:id?", {}) currently gives the following type error:
To match the runtime behavior of generatePath, we should instead be using the type params: { [key in PathParam<Path>]?: string | null | undefined; }. The ? would allow a parameter to be omitted, and the | undefined would allow it to be undefined.
Note that once this change is made, the default value for params could also be simplified from = {} as any to just = {}, since {} would now be a valid value for the specified type.