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

能否编写递归校验路由值继承父级与本地root的TypeScript类型?

如何用TypeScript类型强制路由路径继承父级/本地root规则?

给定如下路由结构:

const routes = {
  root: '/',
  foo: '/foo',         // ok
  bar: {
    root:'/bar',
    foo: '/bar/foo',   // ok 
    bar: '/bar/:barId',// ok
    baz: '/baz'        // 应该报错
  },
} as const;

我们需要编写TypeScript类型,强制所有路由值必须遵循父级路由root属性或当前节点root属性的路径前缀规则,比如上面的baz: '/baz'因为没有继承bar节点的/bar前缀,应该被TypeScript检测为错误。

解决方案

可以通过递归泛型类型实现这一约束,结合satisfies关键字确保类型检查生效的同时,保留原结构的字面量类型:

// 递归路由节点类型,ParentPath为父级路径前缀,默认根路径
type RouteNode<ParentPath extends string = '/'> = {
  // 当前节点root必须匹配父级路径,根节点允许任意/开头的路径
  root: ParentPath extends '/' ? `/${string}` : ParentPath;
} & {
  // 处理非root的其他路由属性
  [K in string]: K extends 'root' 
    ? never  // 禁止重复定义root属性
    : string extends K 
      ? never  // 禁止动态字符串键
      : // 子属性要么是子路由节点(递归传递当前root作为父路径),要么是符合前缀规则的路径字符串
        RouteNode<`${ParentPath extends '/' ? '' : ParentPath}/${string}`> | 
        `${ParentPath extends '/' ? '' : ParentPath}/${string}`;
};

// 应用类型约束
const routes = {
  root: '/',
  foo: '/foo',         // ✅ 符合根路径前缀规则
  bar: {
    root: '/bar',
    foo: '/bar/foo',   // ✅ 符合bar节点的/bar前缀
    bar: '/bar/:barId',// ✅ 带参数的路径也符合前缀规则
    baz: '/baz'        // ❌ 报错:类型"/baz"不符合`/bar/${string}`的前缀要求
  },
} as const satisfies RouteNode;

类型说明

  1. 递归约束:RouteNode是泛型递归类型,每个子节点会自动继承父节点的路径前缀
  2. Root属性校验:当前节点的root必须匹配父级路径,根节点的root允许任意/开头的路径
  3. 子路径校验:所有非root的路由属性,要么是子路由节点(继续递归约束),要么是字符串路径,且必须以父级路径前缀开头
  4. 保留字面量类型:使用as const satisfies RouteNode,既保证类型约束生效,又保留原对象的字面量类型(方便后续路由跳转等场景直接使用)

内容的提问来源于stack exchange,提问作者Matthieu Riegler

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 11:50:32