Skip to content
edge-language-tools
Esc
navigateopen⌘Jpreview
On this page

Typed routes

A @link component whose href autocompletes from your site's real routes.

TypeScript autocompletes string-literal unions inside quotes. Declare your routes as one, reference it from a component’s @types, and every @link call site gets route autocomplete and dead-link squiggles — with zero new tooling.

Declare the routes as a union

// Generated by a script, or hand-written to start.
export type AppRoute = '/' | '/profile' | '/settings' | '/posts'

Hand-write it in five minutes, or generate it — from AdonisJS (node ace list:routes --json), a file-based pages directory, or wherever your routes live. It’s one type alias; any script that can list your routes can emit it.

Reference it from the component

{{--
@name Link
@desc Site-internal anchor; href is checked against the route manifest.
@types {
  to: import('#gen/routes').AppRoute
  label: string
}
--}}
<a href="{{ to }}">{{ label }}</a>

Every call site is now checked

@link({ to: '/profil', label: 'Profile' })
@end

'/profil' gets a red squiggle — Type ‘“/profil”’ is not assignable to type ‘AppRoute’ — in the editor and from edge-check in CI. Put the cursor inside the quotes and the editor offers '/', '/profile', '/settings', '/posts'.

Delete a route from the union and every template still linking to it errors. Dead links become a compile error.

The fancy version: dynamic segments

Template-literal types make parameterized routes checkable too:

export type AppRoute = '/' | '/profile' | `/users/${string}` | `/posts/${number}`

@link({ to: '/users/42' }) passes; @link({ to: '/user/42' }) fails. With a router that exposes route names and params (AdonisJS does), a generator could go further and emit a typed route() helper signature.

Was this page helpful?