如何为React路由权限组件定义合法Action-Subject配对类型?
实现路由权限包装组件的类型安全问题
需求背景
我正在实现一个给路由页面用的通用权限包装组件,逻辑是:用户有对应权限就渲染页面,否则显示NotFoundView。
现有实现代码
const RouteCan = ({ action, subject }: RouteCan) => { // 写法一:使用<Can>组件 return ( <> <Can I={action} as={subject}> <Outlet /> </Can> <Can not I={action} as={subject}> <NotFoundView /> </Can> </> ) // 写法二:使用can函数 // if (can(action, subject)) return <Outlet /> // return <NotFoundView /> }
使用方式
<Route path="/secure-path" element={<RouteCan I="view" as="secure-path" />} > {/* 内部路由 */} </Route>
类型定义问题
我的应用里已经定义了合法的动作-主题配对联合类型:
type Ability = | ['manage' | 'view', 'secure-path'] | ['manage' | 'view' | 'edit', 'public-path']
但直接按下面的方式定义RouteCanProps会出错——它把所有动作和主题混在一起,没有保留合法的配对关系,会允许非法组合(比如I="edit"搭配as="secure-path"):
// 错误定义:无法限制合法配对 type RouteCanProps = { I: Ability[0] as: Ability[1] }
return (<Can>组件和`can`函数仅接受合法的动作-主题配对,因此需要让`RouteCanProps`只能提供**合法的配对组合**,而非单独的动作/主题联合类型。 ## 解决方案 要保留动作与主题的绑定关系,可以通过以下两种方式实现类型安全的Props定义: ### 方法一:用条件类型自动转换配对 通过条件类型将`Ability`的元组类型转换成对应的Props对象联合类型: ```typescript // 将Ability的每个元组映射为合法的Props类型 type AbilityToProps<T extends Ability> = T extends [infer Action, infer Subject] ? { I: Action; as: Subject } : never // RouteCanProps即为所有合法配对的Props联合类型 type RouteCanProps = AbilityToProps<Ability> ``` ### 方法二:显式定义合法Props联合类型 如果觉得自动转换的写法复杂,也可以直接显式写出所有合法的配对组合: ```typescript type RouteCanProps = | { I: 'manage' | 'view'; as: 'secure-path' } | { I: 'manage' | 'view' | 'edit'; as: 'public-path' } ``` 最后更新`RouteCan`组件的类型声明即可: ```tsx const RouteCan = ({ I: action, as: subject }: RouteCanProps) => { // 原有渲染逻辑不变 } ``` 这样就能强制要求传入的动作和主题必须是合法配对,彻底解决类型安全问题。 内容的提问来源于stack exchange,提问作者Mike Kokadii)
相关产品推荐
相关产品推荐

