如何在TypeScript中限制递归类型的嵌套级别?

wwwo4jvm  于 2023-02-13  发布在  TypeScript
关注(0)|答案(2)|浏览(308)

我有一个IMenu接口,它在子菜单项上有一个递归键,如下所示。

interface IMenuItem {
  name: string;
  path: string;
  subMenu?: IMenuItem[]
}

当有人创建这种类型的对象时,我如何将嵌套的级别限制为一个静态值,比如说2个级别,超过这个级别TypeScript应该抛出一个错误。

// 1. Expecting this to work
const menu:IMenuItem[] = [
  { name: 'Dashboard', path: '/' },
  { name: 'Reports', path: '/reports' },
  { 
    name: 'Accounts',
    path: '/',
    subMenu: [
      { name: 'All', path: '/accounts' },
      { name: 'Create', path: '/accounts/create' },
    ],
  },
  { name: 'Settings', path: '/settings' },
];

// 2. How to make this throw an error
const menuAlt:IMenuItem[] = [
  { name: 'Dashboard', path: '/' },
  { name: 'Reports', path: '/reports' },
  { 
    name: 'Accounts',
    path: '/',
    subMenu: [
      { name: 'All', path: '/accounts' },
      {
        name: 'Create',
        path: '/accounts/create',
        // This level show not be accepted
        subMenu: [
          { name: 'admin', path: '/accounts/create-admin' },
          {
            name: 'client',
            path: '/accounts/create-admin',
          },
        ],
      },
    ],
  },
  { name: 'Settings', path: '/settings' },
];

注意:我试着使用Record util,但是对于更高的项目支持,类型失控了。
谢谢你。

u7up0aaq

u7up0aaq1#

下面是使用更多Typescript魔法的另一种方法:

type Length<T extends unknown[]> = 
  T extends { length: infer L } ? L : never;
type BuildTuple<L extends number, T extends unknown[] = []> = 
  T extends { length: L } ? T : BuildTuple<L, [...T, unknown]>;
type MinusOne<N extends number> = 
  BuildTuple<N> extends [...(infer U), unknown]
    ? Length<U>
    : never;

type BaseMenuItem = {
  name: string;
  path: string;
};

type MenuItem<Depth extends number> = Depth extends 0
  ? BaseMenuItem
  : BaseMenuItem & { subMenu?: MenuItem<MinusOne<Depth>>[] };

const x: MenuItem<4> = {
   [...] // Nesting up to 4 times
}

我们首先定义了一个MinusOne实用函数,它接受一个文本整数类型,并将其减1(这需要构建一个元组,从中删除一个元素,然后计算剩余元组的长度),然后我们递归地在每一层删除一个元素,直到达到0。

dfty9e19

dfty9e192#

如果你只想把它限制在两个,我想最简单的方法是做几个类型:

interface IMenuItem {
  name: string;
  path: string;
  subMenu?: ISubMenuItem[]
}

interface ISubMenuItem {
  name: string;
  path: string;
}

除此之外,我不知道有什么简单的方法可以将递归限制在任意级别。
编辑:见我的另一个答案的版本参数的深度。

相关问题