Files
policy-ui/.claude/skills/vue/references/router.md
HaimKortovich a2eb1f3789 Add nuxt-skills and update auto quotes to use new policy API structure
- Add nuxt-skills (vue, nuxt, nuxt-ui) to .claude/skills/
- Create useCustomerSelection() composable for managing insured/buyer selection
- Create usePolicyApi() composable for policy API operations
- Update auto quote components to use insured/buyer instead of client
- Update vehicle fields: remove valorVehiculo, add market_value, requested_value, rc_limits
- Make chassis_number and engine_number optional
- Update auto quote types and composables to match new API structure
- Update auto quote page to submit to policy API with new structure
2026-04-27 14:56:53 -05:00

3.5 KiB

Vue Router Typing

Type-safe routing patterns for Vue Router.

For Nuxt: Use file-based routing instead. See nuxt skill for Nuxt routing patterns.

Route Meta Types

Extend RouteMeta for typed route.meta:

// router/types.ts
import 'vue-router'

declare module 'vue-router' {
  interface RouteMeta {
    requiresAuth?: boolean
    title?: string
    roles?: ('admin' | 'user')[]
  }
}

Usage:

const route = useRoute()
route.meta.requiresAuth // boolean | undefined (typed!)
route.meta.title // string | undefined

Typed Route Params with unplugin-vue-router

Use unplugin-vue-router for fully typed routes:

pnpm add -D unplugin-vue-router
// vite.config.ts
import VueRouter from 'unplugin-vue-router/vite'

export default defineConfig({
  plugins: [VueRouter(), Vue()], // VueRouter BEFORE Vue
})

Typed useRoute:

// Auto-generated route types from file structure
const route = useRoute('/users/[id]')
route.params.id // string (typed!)

const route = useRoute('/posts/[...slug]')
route.params.slug // string[] (typed!)

Typed router.push:

const router = useRouter()

// ✅ Type-checked
router.push({ name: '/users/[id]', params: { id: '123' } })

// ❌ TypeScript error - wrong param
router.push({ name: '/users/[id]', params: { userId: '123' } })

Scroll Behavior Types

Type scroll behavior function:

import type { RouterScrollBehavior } from 'vue-router'

const scrollBehavior: RouterScrollBehavior = (to, from, savedPosition) => {
  if (savedPosition) return savedPosition
  if (to.hash) return { el: to.hash, behavior: 'smooth' }
  return { top: 0 }
}

const router = createRouter({
  history: createWebHistory(),
  routes,
  scrollBehavior,
})

Dynamic Route Params

Handle union types for dynamic segments:

// routes/[type].vue where type can be 'posts' | 'users' | 'comments'
const route = useRoute()

// Narrow params type
type ContentType = 'posts' | 'users' | 'comments'

const type = route.params.type as ContentType

// Or use route guards
if (route.params.type === 'posts') {
  // TypeScript knows type is 'posts'
}

Navigation Guards Types

Type navigation guards:

import type { NavigationGuardWithThis, RouteLocationNormalized } from 'vue-router'

const authGuard: NavigationGuardWithThis<undefined> = (to, from) => {
  if (to.meta.requiresAuth && !isAuthenticated()) {
    return { name: 'login', query: { redirect: to.fullPath } }
  }
}

router.beforeEach(authGuard)

Per-route guards:

const routes = [
  {
    path: '/admin',
    component: AdminPage,
    beforeEnter: (to: RouteLocationNormalized) => {
      if (!hasAdminRole()) return { name: 'forbidden' }
    },
  },
]

RouteLocation Types

Common route types:

import type {
  RouteLocationNormalized,     // Resolved route (after navigation)
  RouteLocationNormalizedLoaded, // Current route (from useRoute)
  RouteLocationRaw,            // Input to router.push()
  RouteRecordRaw,              // Route config definition
} from 'vue-router'

Common Mistakes

Not extending RouteMeta module:

// ❌ Loses type info
route.meta.customField // any

// ✅ Extend the interface
declare module 'vue-router' {
  interface RouteMeta { customField: string }
}

Assuming params are always strings:

// Catch-all routes have string[] params
const route = useRoute()

// ❌ May be string[]
const id = route.params.id

// ✅ Handle both cases
const id = Array.isArray(route.params.id)
  ? route.params.id[0]
  : route.params.id