Quick introduction
Hi! I’m getting more active in the Redwood community because I feel this project has a lot of potential. My focus would be type-safety as you probably have seen my work on TypeScript Project References PR.
Typed Routers
Type safety in routing can be massively beneficial. I’ve encountered so many bugs in production because routes where not on the same page as other parts of the codebase.
Routers such as React Router provide typing systems but they are not fully integrated. Redwood is in a unique position to offer a better more integrated and statically analyzed solution.
State of the art
I’ve used React Router and it’s types from Defiantly Typed. They usually work by handwriting your route type interface:
<Switch>
<Route path="/rooms/{id}" component={Room} />
</Switch>
And then in Room
component you must define the id
manually:
import {RouteComponentProps} from 'react-router';
const Room: React.FC<RouteComponentProps<{ id: string; }>> = () => null;
There is no connection between where the route is defined and where the TypeScript interface for the route is defined. if I decide to rename the parameter in the route the TypeScript definition will be out of date:
<Route path="/rooms/{roomId}" component={Room} />
There is no static analysis system to catch this issue.
Redwood Router
Redwood Routes has the opportunity of statically analyzing the route definitions and generate type definitions based on the routes. A great design decision that has been made is that routes are imported from @redwood/router
. This enables Redwood to emit type definitions for the routes
export every time the route definitions change. Thankfully route parameters have a type interface (Int
for example). This makes the route type definition even more strongly typed as oppose to React Router in which route params are always string.
Generating route type definitions also benefits JavaScript users that use editors like VSCode. The routes will show up in editor suggestion as user types in.
Nice thing about generating types into node_modules is that nothing from the user perspective changes!
The same example above will be:
import { Router, Route } from '@redwoodjs/router'
const Routes = () => (
<Router>
<Route path="/rooms/{roomId: Int} page={RoomsPage} name="room" />
</Router>
)
export default Routes
import { Link, routes } from '@redwoodjs/router'
const SomePage = () => <Link to={routes.room({ roomId: 123 })} />
And if I change the parameter name to id
, I will get a type error in SomePage
.
This is similar to my RFC for generating GraphQL operation types. I love how Prisma hides ugly generated types from users.
This is not an actual RFC because I have not looked into how we can generate the types. A few things I’m not sure about:
Main question is: Can we run a code gen step every time any route definition changes?
- Will all routes be in a single file?
- How dynamic
path
parameter can be? if it is too dynamic we can’t do static analysis - Maybe we can generate types when
Route
component is invoked? This is problematic because in dev time Route might never get invoked.