+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Part 179 of 354

๐Ÿ“˜ Vue Router with TypeScript: Navigation

Master vue router with typescript: navigation in TypeScript with practical examples, best practices, and real-world applications ๐Ÿš€

๐Ÿš€Intermediate
25 min read

Prerequisites

  • Basic understanding of JavaScript ๐Ÿ“
  • TypeScript installation โšก
  • VS Code or preferred IDE ๐Ÿ’ป

What you'll learn

  • Understand Vue Router with TypeScript fundamentals ๐ŸŽฏ
  • Apply typed routing in real Vue projects ๐Ÿ—๏ธ
  • Debug navigation issues ๐Ÿ›
  • Write type-safe navigation code โœจ

๐ŸŽฏ Introduction

Welcome to this exciting tutorial on Vue Router with TypeScript! ๐ŸŽ‰ In this guide, weโ€™ll explore how to create type-safe, robust navigation systems in Vue 3 applications.

Youโ€™ll discover how TypeScript transforms your Vue Router experience from guesswork to guaranteed safety. Whether youโ€™re building single-page applications ๐ŸŒ, admin dashboards ๐Ÿ–ฅ๏ธ, or complex multi-route systems ๐Ÿ“š, understanding typed routing is essential for writing maintainable, error-free navigation code.

By the end of this tutorial, youโ€™ll feel confident implementing type-safe routing in your Vue projects! Letโ€™s navigate into this adventure! ๐Ÿงญ

๐Ÿ“š Understanding Vue Router with TypeScript

๐Ÿค” What is Vue Router with TypeScript?

Vue Router with TypeScript is like having a GPS system with voice commands that actually understand you! ๐Ÿ—บ๏ธ Think of it as a smart navigation assistant that prevents you from taking wrong turns and guides you safely to your destination.

In Vue.js terms, it provides compile-time safety for all your routing operations - route names, parameters, and navigation guards. This means you can:

  • โœจ Catch routing errors before runtime
  • ๐Ÿš€ Get amazing autocomplete for routes
  • ๐Ÿ›ก๏ธ Prevent broken navigation links
  • ๐Ÿ“– Self-document your appโ€™s navigation structure

๐Ÿ’ก Why Use Typed Vue Router?

Hereโ€™s why developers love type-safe routing:

  1. Route Safety ๐Ÿ”’: No more 404s from typos in route names
  2. Parameter Validation ๐Ÿ’ป: Ensure correct data types for route params
  3. Navigation Guards ๐Ÿ“–: Type-safe authentication and authorization
  4. Refactoring Confidence ๐Ÿ”ง: Change routes without breaking navigation

Real-world example: Imagine building an e-commerce app ๐Ÿ›’. With typed routing, you can guarantee that product IDs are always numbers and user navigation is always valid!

๐Ÿ”ง Basic Syntax and Usage

๐Ÿ“ Setting Up Type-Safe Routes

Letโ€™s start with a friendly setup:

// ๐Ÿ‘‹ Hello, Vue Router with TypeScript!
import { createRouter, createWebHistory } from 'vue-router';
import type { RouteRecordRaw } from 'vue-router';

// ๐ŸŽจ Define our route types
interface AppRouteNames {
  home: 'home';
  about: 'about';
  products: 'products';
  'product-detail': 'product-detail';
  profile: 'profile';
}

// ๐Ÿ—๏ธ Create typed route definitions
const routes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home', // ๐Ÿ  Home sweet home
    component: () => import('@/views/HomeView.vue')
  },
  {
    path: '/about',
    name: 'about', // โ„น๏ธ About us page
    component: () => import('@/views/AboutView.vue')
  },
  {
    path: '/products',
    name: 'products', // ๐Ÿ›๏ธ Product catalog
    component: () => import('@/views/ProductsView.vue')
  },
  {
    path: '/product/:id(\\d+)', // ๐ŸŽฏ Only numbers allowed!
    name: 'product-detail',
    component: () => import('@/views/ProductDetailView.vue'),
    props: true // โœจ Pass route params as props
  }
];

// ๐Ÿš€ Create the router
const router = createRouter({
  history: createWebHistory(),
  routes
});

export default router;

๐Ÿ’ก Explanation: Notice how we define route types and use parameter validation! The (\\d+) ensures only numeric IDs are accepted.

๐ŸŽฏ Type-Safe Route Parameters

Hereโ€™s how to handle route parameters safely:

// ๐Ÿ—๏ธ Define parameter interfaces
interface ProductParams {
  id: string; // ๐Ÿ“ฆ Product ID from URL
}

interface UserParams {
  userId: string; // ๐Ÿ‘ค User identifier
  section?: string; // ๐ŸŽฏ Optional section
}

// ๐ŸŽจ Route configuration with typed params
const routes: RouteRecordRaw[] = [
  {
    path: '/product/:id',
    name: 'product-detail',
    component: ProductDetail,
    // ๐Ÿ”„ Transform string ID to number
    beforeEnter: (to) => {
      const id = Number(to.params.id);
      if (isNaN(id)) {
        return { name: 'not-found' }; // ๐Ÿšซ Invalid ID redirect
      }
    }
  },
  {
    path: '/user/:userId/:section?',
    name: 'user-profile',
    component: UserProfile
  }
];

๐Ÿ’ก Practical Examples

๐Ÿ›’ Example 1: E-commerce Navigation System

Letโ€™s build a real shopping app navigation:

// ๐Ÿ›๏ธ E-commerce route definitions
import type { RouteRecordRaw } from 'vue-router';

// ๐ŸŽฏ Define our app's route structure
interface EcommerceRoutes {
  home: {
    name: 'home';
    params: {};
  };
  category: {
    name: 'category';
    params: { categorySlug: string };
  };
  product: {
    name: 'product';
    params: { productId: string };
  };
  cart: {
    name: 'cart';
    params: {};
  };
  checkout: {
    name: 'checkout';
    params: { step?: 'shipping' | 'payment' | 'review' };
  };
}

// ๐Ÿ—๏ธ Route configuration
const ecommerceRoutes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/HomeView.vue'),
    meta: { title: '๐Ÿ  Welcome to Our Store!' }
  },
  {
    path: '/category/:categorySlug',
    name: 'category',
    component: () => import('@/views/CategoryView.vue'),
    props: true,
    // ๐Ÿ›ก๏ธ Validate category exists
    beforeEnter: async (to) => {
      const category = await validateCategory(to.params.categorySlug as string);
      if (!category) {
        return { name: 'not-found' };
      }
    }
  },
  {
    path: '/product/:productId(\\d+)', // ๐ŸŽฏ Only numeric IDs
    name: 'product',
    component: () => import('@/views/ProductView.vue'),
    props: route => ({
      productId: Number(route.params.productId) // ๐Ÿ”„ Convert to number
    })
  },
  {
    path: '/cart',
    name: 'cart',
    component: () => import('@/views/CartView.vue'),
    meta: { requiresAuth: false } // ๐Ÿ›’ Guest checkout allowed
  },
  {
    path: '/checkout/:step?',
    name: 'checkout',
    component: () => import('@/views/CheckoutView.vue'),
    meta: { requiresAuth: true }, // ๐Ÿ”’ Login required
    beforeEnter: (to) => {
      const validSteps = ['shipping', 'payment', 'review'];
      const step = to.params.step as string;
      
      if (step && !validSteps.includes(step)) {
        return { name: 'checkout', params: { step: 'shipping' } };
      }
    }
  }
];

// ๐ŸŽฎ Type-safe navigation helper
class EcommerceNavigator {
  constructor(private router: Router) {}
  
  // ๐Ÿ  Navigate to home
  goHome(): void {
    this.router.push({ name: 'home' });
    console.log('๐Ÿ  Navigating to home!');
  }
  
  // ๐Ÿ›๏ธ Navigate to category
  goToCategory(categorySlug: string): void {
    this.router.push({ 
      name: 'category', 
      params: { categorySlug } 
    });
    console.log(`๐Ÿ“‚ Browsing ${categorySlug} category!`);
  }
  
  // ๐Ÿ“ฆ Navigate to product
  goToProduct(productId: number): void {
    this.router.push({ 
      name: 'product', 
      params: { productId: productId.toString() } 
    });
    console.log(`๐Ÿ›’ Viewing product #${productId}!`);
  }
  
  // ๐Ÿ’ณ Navigate to checkout
  goToCheckout(step: 'shipping' | 'payment' | 'review' = 'shipping'): void {
    this.router.push({ 
      name: 'checkout', 
      params: { step } 
    });
    console.log(`๐Ÿ’ณ Checkout step: ${step}!`);
  }
}

๐ŸŽฏ Try it yourself: Add a search results route with query parameters for filters!

๐ŸŽฎ Example 2: Admin Dashboard Navigation

Letโ€™s create a complex admin system:

// ๐Ÿข Admin dashboard routing
interface AdminRoutes {
  dashboard: { name: 'admin-dashboard'; params: {} };
  users: { name: 'admin-users'; params: {} };
  'user-detail': { name: 'admin-user-detail'; params: { userId: string } };
  settings: { name: 'admin-settings'; params: { section?: string } };
  reports: { name: 'admin-reports'; params: { type: string; dateRange?: string } };
}

// ๐Ÿ›ก๏ธ Permission-based navigation guards
interface UserPermissions {
  canViewUsers: boolean;
  canEditUsers: boolean;
  canViewReports: boolean;
  canAccessSettings: boolean;
}

class AdminNavigationGuard {
  constructor(private permissions: UserPermissions) {}
  
  // ๐Ÿ”’ Check if user can access route
  canAccess(routeName: string): boolean {
    const accessMap: Record<string, keyof UserPermissions> = {
      'admin-users': 'canViewUsers',
      'admin-user-detail': 'canViewUsers',
      'admin-reports': 'canViewReports',
      'admin-settings': 'canAccessSettings'
    };
    
    const permission = accessMap[routeName];
    return permission ? this.permissions[permission] : true;
  }
}

// ๐ŸŽฏ Advanced route configuration
const adminRoutes: RouteRecordRaw[] = [
  {
    path: '/admin',
    component: () => import('@/layouts/AdminLayout.vue'),
    meta: { requiresAuth: true, role: 'admin' }, // ๐Ÿ›ก๏ธ Admin only
    children: [
      {
        path: '',
        name: 'admin-dashboard',
        component: () => import('@/views/admin/DashboardView.vue'),
        meta: { title: '๐Ÿ“Š Admin Dashboard' }
      },
      {
        path: 'users',
        name: 'admin-users',
        component: () => import('@/views/admin/UsersView.vue'),
        meta: { title: '๐Ÿ‘ฅ User Management', permission: 'canViewUsers' }
      },
      {
        path: 'users/:userId(\\d+)',
        name: 'admin-user-detail',
        component: () => import('@/views/admin/UserDetailView.vue'),
        props: route => ({
          userId: Number(route.params.userId) // ๐Ÿ”„ Convert to number
        }),
        meta: { title: '๐Ÿ‘ค User Details', permission: 'canViewUsers' }
      },
      {
        path: 'settings/:section?',
        name: 'admin-settings',
        component: () => import('@/views/admin/SettingsView.vue'),
        meta: { title: 'โš™๏ธ Settings', permission: 'canAccessSettings' }
      },
      {
        path: 'reports/:type/:dateRange?',
        name: 'admin-reports',
        component: () => import('@/views/admin/ReportsView.vue'),
        props: true,
        meta: { title: '๐Ÿ“ˆ Reports', permission: 'canViewReports' }
      }
    ]
  }
];

// ๐Ÿš€ Type-safe admin navigator
class AdminNavigator {
  constructor(
    private router: Router,
    private guard: AdminNavigationGuard
  ) {}
  
  // ๐Ÿ“Š Navigate to dashboard
  goToDashboard(): void {
    this.router.push({ name: 'admin-dashboard' });
  }
  
  // ๐Ÿ‘ฅ Navigate to users with permission check
  goToUsers(): boolean {
    if (!this.guard.canAccess('admin-users')) {
      console.warn('๐Ÿšซ Access denied to users section');
      return false;
    }
    
    this.router.push({ name: 'admin-users' });
    return true;
  }
  
  // ๐Ÿ‘ค Navigate to specific user
  goToUser(userId: number): boolean {
    if (!this.guard.canAccess('admin-user-detail')) {
      console.warn('๐Ÿšซ Access denied to user details');
      return false;
    }
    
    this.router.push({ 
      name: 'admin-user-detail', 
      params: { userId: userId.toString() } 
    });
    return true;
  }
  
  // ๐Ÿ“ˆ Navigate to reports
  goToReports(type: string, dateRange?: string): boolean {
    if (!this.guard.canAccess('admin-reports')) {
      console.warn('๐Ÿšซ Access denied to reports');
      return false;
    }
    
    const params: any = { type };
    if (dateRange) params.dateRange = dateRange;
    
    this.router.push({ name: 'admin-reports', params });
    return true;
  }
}

๐Ÿš€ Advanced Concepts

๐Ÿง™โ€โ™‚๏ธ Advanced Topic 1: Route Meta Type Safety

When youโ€™re ready to level up, try this advanced pattern:

// ๐ŸŽฏ Advanced route meta typing
interface RouteMeta {
  title: string;
  requiresAuth?: boolean;
  roles?: string[];
  permissions?: string[];
  breadcrumb?: string[];
  icon?: string;
  hidden?: boolean;
}

// ๐Ÿช„ Type-safe route definitions with meta
declare module 'vue-router' {
  interface RouteMeta extends RouteMeta {}
}

// ๐Ÿš€ Advanced navigation with meta awareness
class MetaAwareNavigator {
  constructor(private router: Router) {}
  
  // ๐Ÿ” Get route meta safely
  getRouteMeta(routeName: string): RouteMeta | null {
    const route = this.router.getRoutes().find(r => r.name === routeName);
    return route?.meta as RouteMeta || null;
  }
  
  // ๐Ÿž Generate breadcrumbs
  generateBreadcrumbs(currentRouteName: string): string[] {
    const meta = this.getRouteMeta(currentRouteName);
    return meta?.breadcrumb || [meta?.title || 'Unknown'];
  }
  
  // ๐Ÿ›ก๏ธ Check route permissions
  canNavigateTo(routeName: string, userRoles: string[]): boolean {
    const meta = this.getRouteMeta(routeName);
    if (!meta?.roles) return true;
    
    return meta.roles.some(role => userRoles.includes(role));
  }
}

๐Ÿ—๏ธ Advanced Topic 2: Dynamic Route Generation

For the brave developers:

// ๐Ÿš€ Dynamic route generation with types
type RouteGenerator<T extends Record<string, any>> = {
  [K in keyof T]: {
    name: K;
    path: string;
    component: () => Promise<any>;
    meta?: RouteMeta;
  }
};

// ๐ŸŽจ Route factory with type safety
class TypedRouteFactory {
  static createCRUDRoutes<T extends string>(
    resource: T,
    basePath: string
  ): RouteRecordRaw[] {
    const resourceUpper = resource.charAt(0).toUpperCase() + resource.slice(1);
    
    return [
      {
        path: basePath,
        name: `${resource}-list` as const,
        component: () => import(`@/views/${resourceUpper}ListView.vue`),
        meta: { title: `๐Ÿ“‹ ${resourceUpper} List` }
      },
      {
        path: `${basePath}/create`,
        name: `${resource}-create` as const,
        component: () => import(`@/views/${resourceUpper}CreateView.vue`),
        meta: { title: `โž• Create ${resourceUpper}` }
      },
      {
        path: `${basePath}/:id(\\d+)`,
        name: `${resource}-detail` as const,
        component: () => import(`@/views/${resourceUpper}DetailView.vue`),
        props: true,
        meta: { title: `๐Ÿ‘๏ธ ${resourceUpper} Details` }
      },
      {
        path: `${basePath}/:id(\\d+)/edit`,
        name: `${resource}-edit` as const,
        component: () => import(`@/views/${resourceUpper}EditView.vue`),
        props: true,
        meta: { title: `โœ๏ธ Edit ${resourceUpper}` }
      }
    ];
  }
}

// ๐ŸŽฎ Usage
const productRoutes = TypedRouteFactory.createCRUDRoutes('product', '/products');
const userRoutes = TypedRouteFactory.createCRUDRoutes('user', '/users');

โš ๏ธ Common Pitfalls and Solutions

๐Ÿ˜ฑ Pitfall 1: Route Parameter Type Confusion

// โŒ Wrong way - assuming types without validation!
function goToProduct(id: string): void {
  router.push({ name: 'product', params: { id } });
  // ๐Ÿ’ฅ What if id is not a valid product ID?
}

// โœ… Correct way - validate and type properly!
function goToProduct(id: number): void {
  if (id <= 0) {
    console.warn('โš ๏ธ Invalid product ID!');
    return;
  }
  
  router.push({ 
    name: 'product', 
    params: { id: id.toString() } // ๐Ÿ”„ Convert to string for URL
  });
  console.log(`๐Ÿ›’ Navigating to product #${id}!`);
}

๐Ÿคฏ Pitfall 2: Forgetting Route Guards

// โŒ Dangerous - no authentication check!
const sensitiveRoutes = [
  {
    path: '/admin',
    name: 'admin',
    component: AdminPanel // ๐Ÿ’ฅ Anyone can access!
  }
];

// โœ… Safe - proper guards in place!
const protectedRoutes = [
  {
    path: '/admin',
    name: 'admin',
    component: AdminPanel,
    meta: { requiresAuth: true, role: 'admin' },
    beforeEnter: (to, from, next) => {
      if (!isAuthenticated() || !hasRole('admin')) {
        console.log('๐Ÿšซ Access denied - redirecting to login');
        next({ name: 'login' });
        return;
      }
      next(); // โœ… Authorized access
    }
  }
];

๐Ÿ”ง Pitfall 3: Route Name Typos

// โŒ Error-prone - string literals everywhere!
function navigate(): void {
  router.push({ name: 'prodcut-detial' }); // ๐Ÿ’ฅ Typo!
}

// โœ… Type-safe - use constants or enums!
const ROUTES = {
  PRODUCT_DETAIL: 'product-detail',
  USER_PROFILE: 'user-profile',
  ADMIN_DASHBOARD: 'admin-dashboard'
} as const;

function navigateToProduct(): void {
  router.push({ name: ROUTES.PRODUCT_DETAIL }); // โœ… No typos possible
}

// ๐Ÿš€ Even better - use TypeScript enums
enum RouteNames {
  ProductDetail = 'product-detail',
  UserProfile = 'user-profile',
  AdminDashboard = 'admin-dashboard'
}

function navigateSafely(): void {
  router.push({ name: RouteNames.ProductDetail }); // ๐ŸŽฏ Fully typed!
}

๐Ÿ› ๏ธ Best Practices

  1. ๐ŸŽฏ Use Route Constants: Define route names as constants to prevent typos
  2. ๐Ÿ“ Validate Parameters: Always validate route parameters before using them
  3. ๐Ÿ›ก๏ธ Implement Guards: Use navigation guards for authentication and authorization
  4. ๐ŸŽจ Type Your Meta: Create interfaces for route meta properties
  5. โœจ Test Navigation: Write tests for your navigation logic
  6. ๐Ÿ”„ Handle Errors: Gracefully handle navigation errors and fallbacks

๐Ÿงช Hands-On Exercise

๐ŸŽฏ Challenge: Build a Blog Navigation System

Create a type-safe blog application with the following features:

๐Ÿ“‹ Requirements:

  • โœ… Home page with featured posts
  • ๐Ÿท๏ธ Category pages with filtered posts
  • ๐Ÿ“ Individual post pages with comments
  • ๐Ÿ‘ค Author profile pages
  • ๐Ÿ” Search results with query parameters
  • ๐Ÿ›ก๏ธ Admin section with authentication

๐Ÿš€ Bonus Points:

  • Add breadcrumb navigation
  • Implement route transitions
  • Create a navigation history tracker
  • Add sharing capabilities with proper URLs

๐Ÿ’ก Solution

๐Ÿ” Click to see solution
// ๐ŸŽฏ Our type-safe blog navigation system!
import type { RouteRecordRaw } from 'vue-router';

// ๐Ÿ“ Blog route interfaces
interface BlogRoutes {
  home: { name: 'home'; params: {} };
  category: { name: 'category'; params: { slug: string } };
  post: { name: 'post'; params: { slug: string } };
  author: { name: 'author'; params: { username: string } };
  search: { name: 'search'; params: {}; query: { q: string; category?: string } };
  'admin-posts': { name: 'admin-posts'; params: {} };
}

// ๐Ÿ—๏ธ Route definitions
const blogRoutes: RouteRecordRaw[] = [
  {
    path: '/',
    name: 'home',
    component: () => import('@/views/HomeView.vue'),
    meta: { 
      title: '๐Ÿ  Blog Home',
      breadcrumb: ['Home']
    }
  },
  {
    path: '/category/:slug',
    name: 'category',
    component: () => import('@/views/CategoryView.vue'),
    props: true,
    meta: { 
      title: '๐Ÿ“‚ Category',
      breadcrumb: ['Home', 'Categories']
    },
    beforeEnter: async (to) => {
      const categoryExists = await validateCategory(to.params.slug as string);
      if (!categoryExists) {
        return { name: 'not-found' };
      }
    }
  },
  {
    path: '/post/:slug',
    name: 'post',
    component: () => import('@/views/PostView.vue'),
    props: true,
    meta: { 
      title: '๐Ÿ“ Post',
      breadcrumb: ['Home', 'Posts']
    }
  },
  {
    path: '/author/:username',
    name: 'author',
    component: () => import('@/views/AuthorView.vue'),
    props: true,
    meta: { 
      title: '๐Ÿ‘ค Author Profile',
      breadcrumb: ['Home', 'Authors']
    }
  },
  {
    path: '/search',
    name: 'search',
    component: () => import('@/views/SearchView.vue'),
    meta: { 
      title: '๐Ÿ” Search Results',
      breadcrumb: ['Home', 'Search']
    }
  },
  {
    path: '/admin',
    component: () => import('@/layouts/AdminLayout.vue'),
    meta: { requiresAuth: true, role: 'admin' },
    children: [
      {
        path: 'posts',
        name: 'admin-posts',
        component: () => import('@/views/admin/PostsView.vue'),
        meta: { 
          title: '๐Ÿ“‹ Manage Posts',
          breadcrumb: ['Home', 'Admin', 'Posts']
        }
      }
    ]
  }
];

// ๐ŸŽฎ Blog navigator class
class BlogNavigator {
  constructor(private router: Router) {}
  
  // ๐Ÿ  Go to home
  goHome(): void {
    this.router.push({ name: 'home' });
  }
  
  // ๐Ÿ“‚ Navigate to category
  goToCategory(slug: string): void {
    this.router.push({ 
      name: 'category', 
      params: { slug } 
    });
  }
  
  // ๐Ÿ“ Navigate to post
  goToPost(slug: string): void {
    this.router.push({ 
      name: 'post', 
      params: { slug } 
    });
  }
  
  // ๐Ÿ‘ค Navigate to author
  goToAuthor(username: string): void {
    this.router.push({ 
      name: 'author', 
      params: { username } 
    });
  }
  
  // ๐Ÿ” Navigate to search with query
  searchPosts(query: string, category?: string): void {
    const searchQuery: any = { q: query };
    if (category) searchQuery.category = category;
    
    this.router.push({ 
      name: 'search', 
      query: searchQuery 
    });
  }
  
  // ๐Ÿž Generate breadcrumbs
  getBreadcrumbs(): string[] {
    const currentRoute = this.router.currentRoute.value;
    return currentRoute.meta?.breadcrumb as string[] || ['Home'];
  }
}

// ๐Ÿ›ก๏ธ Authentication guard
async function adminGuard(to: RouteLocationNormalized): Promise<boolean | RouteLocationRaw> {
  const isAuthenticated = await checkAuthStatus();
  const hasAdminRole = await checkAdminRole();
  
  if (!isAuthenticated) {
    console.log('๐Ÿšซ Please log in to access admin area');
    return { name: 'login', query: { redirect: to.fullPath } };
  }
  
  if (!hasAdminRole) {
    console.log('๐Ÿšซ Admin privileges required');
    return { name: 'home' };
  }
  
  return true;
}

// ๐ŸŽ‰ Usage example
const navigator = new BlogNavigator(router);

// Navigate to different sections
navigator.goToCategory('typescript-tips');
navigator.goToPost('vue-router-typescript-guide');
navigator.searchPosts('TypeScript', 'tutorials');

// Helper functions
async function validateCategory(slug: string): Promise<boolean> {
  // ๐Ÿ” Check if category exists
  const response = await fetch(`/api/categories/${slug}`);
  return response.ok;
}

async function checkAuthStatus(): Promise<boolean> {
  // ๐Ÿ”’ Check authentication status
  const token = localStorage.getItem('auth-token');
  if (!token) return false;
  
  const response = await fetch('/api/auth/verify', {
    headers: { Authorization: `Bearer ${token}` }
  });
  
  return response.ok;
}

async function checkAdminRole(): Promise<boolean> {
  // ๐Ÿ‘‘ Check admin role
  const response = await fetch('/api/user/role');
  const data = await response.json();
  return data.role === 'admin';
}

๐ŸŽ“ Key Takeaways

Youโ€™ve learned so much! Hereโ€™s what you can now do:

  • โœ… Create type-safe Vue Router navigation with confidence ๐Ÿ’ช
  • โœ… Avoid common routing mistakes that trip up beginners ๐Ÿ›ก๏ธ
  • โœ… Apply navigation guards for security ๐Ÿ”’
  • โœ… Debug routing issues like a pro ๐Ÿ›
  • โœ… Build awesome navigation systems with TypeScript! ๐Ÿš€

Remember: Vue Router with TypeScript is your navigation compass, not a burden! Itโ€™s here to help you build reliable, maintainable applications. ๐Ÿค

๐Ÿค Next Steps

Congratulations! ๐ŸŽ‰ Youโ€™ve mastered Vue Router with TypeScript navigation!

Hereโ€™s what to do next:

  1. ๐Ÿ’ป Practice with the blog navigation exercise above
  2. ๐Ÿ—๏ธ Build a multi-page Vue app with typed routing
  3. ๐Ÿ“š Move on to our next tutorial: Vue 3 Composition API with TypeScript
  4. ๐ŸŒŸ Share your navigation patterns with the community!

Remember: Every routing expert was once a beginner who got lost in navigation! Keep coding, keep learning, and most importantly, enjoy the journey! ๐Ÿงญโœจ


Happy routing! ๐ŸŽ‰๐Ÿš€โœจ