← 返回导航

路由配置类型

业务类型 · 实战

场景说明

无论是 vue-router 还是 react-router,路由配置都是前端项目的核心:path、name、component、meta、children、redirect 等字段构成了路由表。路由配置天然是递归结构(children 嵌套子路由),用 TypeScript 类型化后,可以约束 path 格式、meta 字段结构、name 唯一性,甚至从路由配置推导出所有路由名的联合类型,在编程式导航时享有完整的类型提示。

完整代码


      
// 路由配置类型:递归嵌套 + meta 强类型 + 路由名联合推导
// children 递归引用自身,meta 泛型化,从配置推导出 name 联合

// 1. 路由元信息基础类型
interface RouteMetaBase {
  title?: string;
  icon?: string;
  requiresAuth?: boolean;
  roles?: string[];
  keepAlive?: boolean;
  hidden?: boolean;
}

// 2. 路由配置:泛型 M 约束 meta 类型,递归 children
interface RouteConfig<M = RouteMetaBase> {
  path: string;
  name?: string;
  component?: any;
  redirect?: string;
  meta?: M;
  children?: RouteConfig<M>[];  // 递归引用:子路由与父路由同类型
  props?: boolean | Record<string, any> | ((...args: any[]) => Record<string, any>);
  alias?: string | string[];
  beforeEnter?: (...args: any[]) => any;
}

// 3. 从路由配置数组中提取所有 name 的联合类型
type RouteNames<T extends readonly RouteConfig[]> =
  T[number] extends infer R
    ? R extends { name: string; children?: infer C }
      ? C extends readonly RouteConfig[]
        ? R['name'] | RouteNames<C>
        : R['name']
      : never
    : never;

// 4. 自定义 meta 类型
interface AppRouteMeta extends RouteMetaBase {
  title: string;                         // 自定义:title 必填
  affix?: boolean;                       // 自定义:是否固钉
  breadcrumb?: boolean;                   // 自定义:是否显示面包屑
  activeMenu?: string;                    // 自定义:高亮菜单
}

// 5. 业务路由配置类型
type AppRouteConfig = RouteConfig<AppRouteMeta>;

// 6. 路由表示例
const routes = [
  {
    path: '/',
    name: 'Home',
    component: 'HomeView',
    meta: { title: '首页', icon: 'home', affix: true },
  },
  {
    path: '/user',
    name: 'User',
    component: 'UserLayout',
    meta: { title: '用户管理', icon: 'user', requiresAuth: true },
    children: [
      {
        path: 'list',
        name: 'UserList',
        component: 'UserList',
        meta: { title: '用户列表', icon: 'list' },
      },
      {
        path: 'detail/:id',
        name: 'UserDetail',
        component: 'UserDetail',
        meta: { title: '用户详情', hidden: true, activeMenu: '/user/list' },
      },
    ],
  },
  {
    path: '/about',
    name: 'About',
    component: 'AboutView',
    meta: { title: '关于', icon: 'info' },
  },
  {
    path: '/:pathMatch(.*)*',
    redirect: '/404',
    meta: { title: '404', hidden: true },
  },
] as const;

// 7. 推导出所有路由名的联合类型
type AppRouteName = RouteNames<typeof routes>;
// => 'Home' | 'User' | 'UserList' | 'UserDetail' | 'About'

// 8. 编程式导航函数(类型安全)
interface RouteLocation {
  name?: AppRouteName;
  path?: string;
  params?: Record<string, string | number>;
  query?: Record<string, string | number>;
}

declare function push(to: RouteLocation): Promise<void>;

// 使用:路由名有类型提示和校验
function goUserDetail(id: number) {
  push({ name: 'UserDetail', params: { id } });
}

关键点解析

使用示例与类型推导


      
// 路由名联合类型推导
type Names = AppRouteName;
// => 'Home' | 'User' | 'UserList' | 'UserDetail' | 'About'

// 编程式导航时 name 只能是已知路由
// push({ name: 'UserDetai' });  // 错误:拼写错误直接被 TS 拦截

// meta 字段类型检查
const route: AppRouteConfig = {
  path: '/test',
  name: 'Test',
  meta: {
    title: '测试',  // 必填,缺了会报错
    affix: true,    // 自定义字段可用
    // unknownField: 123,  // 错误:未知字段
  },
};

// 子路由同样受类型约束
const nested: AppRouteConfig = {
  path: '/parent',
  name: 'Parent',
  meta: { title: '父路由' },
  children: [
    {
      path: 'child',
      name: 'Child',
      meta: { title: '子路由' },
      children: [
        {
          path: 'grandchild',
          name: 'Grandchild',
          meta: { title: '孙子路由' },
        }
      ]
    }
  ]
};

// 三级嵌套路由的 name 也能被提取
type NestedNames = RouteNames<typeof [nested]>;
// => 'Parent' | 'Child' | 'Grandchild'
在 Playground 中尝试