React TypeScript:扩展React-Router V6 RouteObject接口添加name字段遇类型错误求助
嗨,我来帮你梳理下这个问题的原因和解决办法~
首先先指出你代码里一个小错误:你的routes变量应该声明为IRouteObject[]类型(因为是数组),而不是IRouteObject,这是一个基础的类型声明错误,先修正这个:
const routes: IRouteObject[] = [ // ... 你的路由配置 ];
接下来是核心问题:为什么matchRoutes返回的子路由还是RouteObject[]类型?
这是因为React-Router内置的matchRoutes函数的类型定义是基于它自己的RouteObject接口的,即使你传入了自定义的IRouteObject[]数组,函数内部处理后返回的route对象仍然会被TypeScript推断为原始的RouteObject类型,包括children属性也会被识别为RouteObject[],所以访问name字段会报错。
下面是两种可行的解决方式:
方式一:使用类型断言(快速解决)
在访问childRoute.name的时候,把childRoute断言为你的自定义类型IRouteObject,这样TypeScript就会认可name字段的存在:
childRoutes?.map((childRoute, index) => { console.log((childRoute as IRouteObject).name); });
⚠️ 注意:只有当你确定所有childRoutes里的对象都严格符合IRouteObject的结构时,这种方式才是安全的。
方式二:模块扩充(Module Augmentation,推荐方案)
这是更优雅的方式,直接全局扩展React-Router的RouteObject接口,让所有地方的RouteObject都自动包含name字段,不需要自定义额外的接口:
- 在你的项目中创建一个类型声明文件(比如
src/types/react-router.d.ts),或者在已有类型文件中添加以下代码:
import 'react-router-dom'; declare module 'react-router-dom' { interface RouteObject { // 用可选属性?,避免影响原有未使用name字段的路由配置 name?: string; } }
- 之后你就可以直接在路由配置中添加
name字段,不需要自定义IRouteObject了:
const routes: RouteObject[] = [ { path: '/forms', element: <FormPage />, caseSensitive: true, name: 'forms', children: [ { path: '/', element: <p>Name Form</p>, caseSensitive: true, name: 'Name form' }, ] } ];
- 此时
matchRoutes返回的childRoute会自动包含name字段,访问childRoute.name不会再报错:
const Nav: React.FC<{}> = () => { let location = useLocation(); let routeMatches = matchRoutes(routes, location); if (routeMatches && routeMatches.length > 1) { const childRoutes = routeMatches[0].route.children; childRoutes?.map((childRoute, index) => { console.log(childRoute.name); // 现在TypeScript不会报错了 }) } return null; // 记得返回React元素 }
这种方式的优势在于,你不需要在项目中到处使用自定义的IRouteObject类型,所有React-Router相关的路由对象都会自动带上name字段的类型支持,更符合TypeScript的类型扩展规范。
内容的提问来源于stack exchange,提问作者bp123

