feat: improve param type inference
This PR resolves #1053 by adding automatic type inference for route parameters. When you define a route with parameters using app.get(), app.post(), or any other HTTP method, TypeScript now knows exactly what parameters are available in event.context.params.
Previously, event.context.params was always typed as Record<string, string> | undefined, even when the route pattern clearly defined specific parameters. Now the route pattern is parsed at the type level to extract parameter names and provide full type safety.
Route parameters are now fully typed based on the route pattern you define:
// Before: params were loosely typed
app.get("/user/:id", (event) => {
const id = event.context.params.id; // string | undefined
});
// After: params are inferred from the route
app.get("/user/:id", (event) => {
const id = event.context.params.id; // string ✨
});
This works with multiple parameters too:
app.get("/user/:userId/post/:postId", (event) => {
event.context.params.userId; // string
event.context.params.postId; // string
});
The existing helper functions (getRouterParam and getRouterParams) also benefit from this:
app.get("/hello/:name", (event) => {
const params = getRouterParams(event); // { name: string }
const name = getRouterParam(event, "name"); // string
});
Type inference works across all route registration methods like app.get(), app.post(), app.put(), app.delete(), and app.on(). Routes without parameters have params typed as undefined, so you'll know when there are no parameters available.
When you use defineHandler directly (outside of a route), the route pattern isn't available yet, so params remain untyped as Record<string, string> | undefined. You can still manually type them using the routerParams field in EventHandlerRequest if needed. However, when you inline defineHandler with a route, the params are fully typed automatically:
app.get("/hello/:name", defineHandler((event) => {
event.context.params.name; // string - fully typed!
const params = getRouterParams(event); // { name: string }
const name = getRouterParam(event, "name"); // string
}));
The implementation leverages InferRouteParams from rou3 (https://github.com/h3js/rou3/pull/168) for route pattern parsing. I have tried to make the types backward compatible, so if you catch something that doesn't work as before, just tell me and i'll fix it 😅
This is an awesome start. Wondering if we could pair it with InferRouteParams from rou3 (https://github.com/h3js/rou3/pull/168) for app.[method] somehow
This is an awesome start. Wondering if we could pair it with
InferRouteParamsfrom rou3 (h3js/rou3#168) forapp.[method]somehow
Yea, i have already started working on it locally. But ran into some beahviour issues which i am trying to figure out first 👍🏻
Will include it in this PR when i am done.
@pi0 This PR should be ready for a quick review when you have a moment - happy to adjust anything if needed 😊
I have tried cleaning the overloads from f7c9ece (#1217) up in 42a9512 (#1217), let me know if i should revert that to the multiple inline overloads approach 👍🏻
Sorry, it got delayed @luxass, we will try to review soon (also added @danielroe)
Dear @luxass you don't need to rebase PR on all commits. I can take care of rebase before merge 👍🏼