Skip to content

Commit 7ca1e40

Browse files
authored
Merge pull request #12019 from remix-run/pedro/typesafety-phase-1
Typesafety improvements
2 parents 1dc2ede + 7e87c5d commit 7ca1e40

35 files changed

+1114
-85
lines changed

.changeset/typesafety.md

+138
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
---
2+
"@react-router/dev": minor
3+
"react-router": minor
4+
---
5+
6+
### Typesafety improvements
7+
8+
React Router now generates types for each of your route modules.
9+
You can access those types by importing them from `./+types/<route filename without extension>`.
10+
For example:
11+
12+
```ts
13+
// app/routes/product.tsx
14+
import type * as Route from "./+types/product";
15+
16+
export function loader({ params }: Route.LoaderArgs) {}
17+
18+
export default function Component({ loaderData }: Route.ComponentProps) {}
19+
```
20+
21+
This initial implementation targets type inference for:
22+
23+
- `Params` : Path parameters from your routing config in `routes.ts` including file-based routing
24+
- `LoaderData` : Loader data from `loader` and/or `clientLoader` within your route module
25+
- `ActionData` : Action data from `action` and/or `clientAction` within your route module
26+
27+
These types are then used to create types for route export args and props:
28+
29+
- `LoaderArgs`
30+
- `ClientLoaderArgs`
31+
- `ActionArgs`
32+
- `ClientActionArgs`
33+
- `HydrateFallbackProps`
34+
- `ComponentProps` (for the `default` export)
35+
- `ErrorBoundaryProps`
36+
37+
In the future, we plan to add types for the rest of the route module exports: `meta`, `links`, `headers`, `shouldRevalidate`, etc.
38+
We also plan to generate types for typesafe `Link`s:
39+
40+
```tsx
41+
<Link to="/products/:id" params={{ id: 1 }} />
42+
// ^^^^^^^^^^^^^ ^^^^^^^^^
43+
// typesafe `to` and `params` based on the available routes in your app
44+
```
45+
46+
#### Setup
47+
48+
React Router will generate types into a `.react-router/` directory at the root of your app.
49+
This directory is fully managed by React Router and is derived based on your route config (`routes.ts`).
50+
51+
👉 ** Add `.react-router/` to `.gitignore` **
52+
53+
```txt
54+
.react-router
55+
```
56+
57+
You should also ensure that generated types for routes are always present before running typechecking,
58+
especially for running typechecking in CI.
59+
60+
👉 ** Add `react-router typegen` to your `typecheck` command in `package.json` **
61+
62+
```json
63+
{
64+
"scripts": {
65+
"typecheck": "react-router typegen && tsc"
66+
}
67+
}
68+
```
69+
70+
To get TypeScript to use those generated types, you'll need to add them to `include` in `tsconfig.json`.
71+
And to be able to import them as if they files next to your route modules, you'll also need to configure `rootDirs`.
72+
73+
👉 ** Configure `tsconfig.json` for generated types **
74+
75+
```json
76+
{
77+
"include": [".react-router/types/**/*"],
78+
"compilerOptions": {
79+
"rootDirs": [".", "./.react-router/types"]
80+
}
81+
}
82+
```
83+
84+
#### `typegen` command
85+
86+
You can manually generate types with the new `typegen` command:
87+
88+
```sh
89+
react-router typegen
90+
```
91+
92+
However, manual type generation is tedious and types can get out of sync quickly if you ever forget to run `typegen`.
93+
Instead, we recommend that you setup our new TypeScript plugin which will automatically generate fresh types whenever routes change.
94+
That way, you'll always have up-to-date types.
95+
96+
#### TypeScript plugin
97+
98+
To get automatic type generation, you can use our new TypeScript plugin.
99+
100+
👉 ** Add the TypeScript plugin to `tsconfig.json` **
101+
102+
```json
103+
{
104+
"compilerOptions": {
105+
"plugins": [{ "name": "@react-router/dev" }]
106+
}
107+
}
108+
```
109+
110+
We plan to add some other goodies to our TypeScript plugin soon, including:
111+
112+
- Automatic `jsdoc` for route exports that include links to official docs
113+
- Autocomplete for route exports
114+
- Warnings for non-HMR compliant exports
115+
116+
##### VSCode
117+
118+
TypeScript looks for plugins registered in `tsconfig.json` in the local `node_modules/`,
119+
but VSCode ships with its own copy of TypeScript that is installed outside of your project.
120+
For TypeScript plugins to work, you'll need to tell VSCode to use the local workspace version of TypeScript.
121+
122+
👉 ** Ensure that VSCode is using the workspace version of TypeScript **
123+
124+
This should already be set up for you by a `.vscode/settings.json`:
125+
126+
```json
127+
{
128+
"typescript.tsdk": "node_modules/typescript/lib"
129+
}
130+
```
131+
132+
Alternatively, you can open up any TypeScript file and use <kbd>CMD</kbd>+<kbd>SHIFT</kbd>+<kbd>P</kbd> to find `Select TypeScript Version` and then select `Use Workspace Version`. You may need to quit VSCode and reopen it for this setting to take effect.
133+
134+
##### Troubleshooting
135+
136+
In VSCode, open up any TypeScript file in your project and then use <kbd>CMD</kbd>+<kbd>SHIFT</kbd>+<kbd>P</kbd> to select `Open TS Server log`.
137+
There should be a log for `[react-router] setup` that indicates that the plugin was resolved correctly.
138+
Then look for any errors in the log.

.vscode/settings.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules/typescript/lib"
3+
}

decisions/0003-infer-types-for-useloaderdata-and-useactiondata-from-loader-and-action-via-generics.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
Date: 2022-07-11
44

5-
Status: accepted
5+
Status: Superseded by [#0012](./0012-type-inference.md)
66

77
## Context
88

0 commit comments

Comments
 (0)