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
相关产品推荐
相关产品推荐

