You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为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)
相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 07:42:44