Typed Routes

With "experiments": { "typedRoutes": true } in app.json, npx expo start scans src/app/ and writes .expo/types/router.d.ts, a union of every valid href, from / and /cart to /book/[id] with its id parameter. It also creates expo-env.d.ts, adds it to .gitignore, and adds both files to tsconfig.json's include list. Still labeled beta, it catches links to routes that don't exist when you type-check. Typing Props and Params showed a mistyped path being caught; parameter names are checked too. A scratch file with two mistakes, router.push('/books/3') and router.push({ pathname: '/book/[id]', params: { bookId: 3 } }), gave:

Output of 149
$ npx tsc --noEmit
src/TypoCheck.tsx(4,15): error TS2345: Argument of type '"/books/3"' is not assignable to param
eter of type 'RelativePathString | ExternalPathString | "/checkout" | `/checkout?${string}` | `
...
src/TypoCheck.tsx(5,41): error TS2322: Type '{ bookId: number; }' is not assignable to type 'Un
knownInputParams & { id: string | number; }'.
  Property 'id' is missing in type '{ bookId: number; }' but required in type '{ id: string | n
umber; }'.

An href accepts id as a string or a number, but useLocalSearchParams<'/book/[id]'>() types it as a string, which is what arrives (Dynamic Routes). The types are only as fresh as the last expo start, so on a CI server run npx expo customize tsconfig.json first, which generates them without starting the dev server.