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

TypeScript namespace扩展Enum报不可迭代错误如何解决

TypeScript enum合并namespace后运行时报迭代错误

更新:可通过在线Playground复现问题。

经初步排查,问题与namespace内函数的export修饰符直接相关:移除该export关键字后代码可正常运行。


业务背景

我正在为应用搭建权限控制模块,需要实现权限层级继承逻辑:

  • moderator角色自动继承jrMod的所有权限
  • jrMod角色自动继承babyMod的所有权限
    调用Member.hasPermission(MemberPermission.jrMod)时,方法会自动校验用户是否持有jrMod、moderator权限,同时也会校验最高级的tech、admin权限。

问题表现

如果移除代码中namespace扩展Enum的部分,代码可正常运行;如果保留该部分,会在标记的代码行抛出错误:

(intermediate value)(intermediate value)(intermediate value) is not iterable

如果改用push方法实现相同的数组合并逻辑,会抛出错误:

Found non-callable @@iterator

可复现问题的完整代码如下:

enum MemberPermission {
  tech = 'tech', // 最高权限
  admin = 'admin', // 次高权限
  moderator = 'moderator',
  jrMod = 'jrMod',
  babyMod = 'babyMod'
};

namespace MemberPermission {
  export function fromString(perm: string): MemberPermission {
    return (MemberPermission as any)[perm];
  }
}

type NonPrivilegedPermissions = Exclude<MemberPermission, "admin" | "tech">
let MemberPermissionMapSetup: {
  [key in NonPrivilegedPermissions]?: NonPrivilegedPermissions[]
} = {
  moderator: [MemberPermission.jrMod],
  jrMod: [MemberPermission.babyMod]
};

let MemberPermissionMap: typeof MemberPermissionMapSetup = {};

function spiderPermissions(mp: keyof typeof MemberPermissionMapSetup): NonPrivilegedPermissions[] {
  if (MemberPermissionMapSetup[mp] != undefined) {

    // V V V V 报错位置
    MemberPermissionMap[mp] = [...MemberPermissionMap[mp] ?? [], ...MemberPermissionMapSetup[mp]!];
    MemberPermissionMapSetup[mp]!.forEach((child, v) => {
      MemberPermissionMap[mp]!.push(...spiderPermissions(child as NonPrivilegedPermissions))
    })
  }
  MemberPermissionMap[mp] = Array.from(new Set(MemberPermissionMap[mp]));
  return MemberPermissionMap[mp] ?? [];
}

for (let p in MemberPermission) {
  spiderPermissions(p as NonPrivilegedPermissions);
}

console.log(MemberPermissionMap);

错误原因

给enum合并namespace并导出内部函数时,for...in遍历enum对象会把namespace导出的fromString函数也枚举出来,不会只遍历enum本身的成员。
代码中用p as NonPrivilegedPermissions做强制类型转换,把遍历到的"fromString"字符串当成合法权限值传入了spiderPermissions函数:

  • 此时MemberPermissionMapSetup["fromString"]是undefined,不会进入if判断的分支
  • 后续执行Array.from(new Set(MemberPermissionMap[mp]))时,MemberPermissionMap["fromString"]为undefined,new Set接收undefined作为入参就会抛出迭代相关错误,和遇到的报错完全匹配。
    之前测试移除export后代码正常运行,本质是因为未导出的namespace成员不会挂载到enum对象上,for...in遍历不到,自然不会触发问题。

修复方案

两种方案任选即可,优先推荐第二种:

  • 方案1:遍历enum时增加校验,过滤掉非权限的额外属性,不要靠强制类型断言跳过检查:
for (let p in MemberPermission) {
  if (p in MemberPermissionMapSetup) {
    spiderPermissions(p as NonPrivilegedPermissions);
  }
}
  • 方案2:放弃用for...in遍历enum,直接遍历提前定义好的权限映射表的键,从根源上避免遍历到挂载的函数:
for (const p of Object.keys(MemberPermissionMapSetup) as NonPrivilegedPermissions[]) {
  spiderPermissions(p);
}

内容的提问来源于stack exchange,提问作者Regular Jo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 07:27:17