Type Safety
The Gea router infers types from your route config. No codegen, no manual type definitions. The route object is the single source of truth.
as const
The foundation of type inference. Without as const, TypeScript widens string literals and the router can't infer param names or child keys.
// Without as const — types are too wide, no inference
export const router = createRouter({
'/projects/:id': Project,
})
// With as const — full type inference
export const router = createRouter({
'/projects/:id': Project,
} as const)Always add as const to your route config.
InferRouteProps
Extracts layout prop types from the router. Export it once from your router file.
// router.ts
import { createRouter, InferRouteProps } from '@geajs/core'
export const router = createRouter({
'/': {
layout: AppShell,
children: {
'/dashboard': {
layout: DashboardLayout,
children: {
'/': Overview,
'/projects': Projects,
'/projects/:id': Project,
}
},
'/settings': {
layout: SettingsLayout,
mode: { type: 'query', param: 'view' },
children: {
'profile': ProfileSettings,
'billing': BillingSettings,
'team': TeamSettings,
}
},
}
},
'*': NotFound,
} as const)
export type RouteProps = InferRouteProps<typeof router>RouteProps
Use RouteProps['/path'] to type a layout component. The type is keyed by the route path where the layout is mounted.
import type { RouteProps } from '../router'
export default class DashboardLayout extends Component<RouteProps['/dashboard']> {
template({ page, route, params }) {
// page: Overview | Projects | Project — union of children
// route: string — matched pattern
// params: { id?: string } — union of all child params
}
}For query-mode layouts:
export default class SettingsLayout extends Component<RouteProps['/settings']> {
template({ page, activeKey, keys, navigate }) {
// activeKey: 'profile' | 'billing' | 'team'
// keys: ('profile' | 'billing' | 'team')[]
// navigate: (key: 'profile' | 'billing' | 'team') => void
}
}The types follow the route config. Add a child route, and the layout's page union updates automatically.
ExtractParams
Extracts param types from a route pattern string.
import type { ExtractParams } from '@geajs/core'
type Params = ExtractParams<'/projects/:id'>
// { id: string }
type Nested = ExtractParams<'/users/:userId/posts/:postId'>
// { userId: string; postId: string }This is used internally by InferRouteProps. You rarely need it directly, but it's available for edge cases where you need param types outside of a layout.
Putting It Together
// router.ts
import { createRouter, InferRouteProps } from '@geajs/core'
// ... imports
export const router = createRouter({
'/login': Login,
'/': {
layout: AppShell,
guard: AuthGuard,
children: {
'/dashboard': {
layout: DashboardLayout,
children: {
'/': Overview,
'/projects': Projects,
'/projects/:id': Project,
}
},
}
},
'*': NotFound,
} as const)
export type RouteProps = InferRouteProps<typeof router>// layouts/DashboardLayout.tsx
import { Component } from '@geajs/core'
import type { RouteProps } from '../router'
export default class DashboardLayout extends Component<RouteProps['/dashboard']> {
template({ page, params }) {
// TypeScript knows:
// - page is Overview | Projects | Project
// - params.id is string | undefined (present for Project, absent for others)
return (
<div class="dashboard">
<nav>...</nav>
<main>{page}</main>
</div>
)
}
}// views/Project.tsx — route components get typed params too
import { Component } from '@geajs/core'
export default class Project extends Component {
created({ id }) {
// id: string — inferred from '/projects/:id'
projectStore.fetch(id)
}
}Recommendations
- Always use
as conston the route config. Without it, nothing else works. - Export
RoutePropsonce fromrouter.ts. Every layout imports the same type. - Type layouts with
RouteProps['/path'], not manual interfaces. The types stay in sync with the config automatically. - Don't use
ExtractParamsunless you have a specific need outside of layout typing.RoutePropscovers the common case. - If TypeScript complains about the route config being too complex, break child objects into named constants with
as conston each, then compose them.
