TypeScript递归查找级联子对象方法无限递归问题修复求助
问题根源
你现有代码出现无限递归和数据异常的核心原因有3个:
- 全程复用同一个
currentObject对象引用,导致数组中存储的所有条目都会被后续循环的值覆盖 - 去重逻辑仅判断已存入全局数组的条目,未处理「A→B→A」这类循环引用场景,子对象还未存入数组就已经触发递归,就会进入死循环
- 递归返回的是全局
objectsArray本身,每次展开...result会重复推送大量重复数据,同时全局数组跨多个根对象共享,会导致多入口查询结果互相污染
修复后代码
1. 修改findChildObjects方法
export interface ArchiveObject { objectName?: string; referenceField?: string; referenceTo?: string; isDelete?: boolean; } public findChildObjects( selectedObject: string, globalDescribeMap: Map<string, SFDescribeObjects>, // 递归内部传递的已访问标记,外部调用无需传参 visited = new Set<string>() ): ArchiveObject[] { const result: ArchiveObject[] = []; const objectDescribe = globalDescribeMap.get(selectedObject); if (!objectDescribe) return result; objectDescribe.childRelationships.forEach(childRel => { // 用「子对象名+关联字段」作为唯一标识,避免同一关联重复处理 const relKey = `${childRel.childSObject}###${childRel.field}`; if (visited.has(relKey)) return; visited.add(relKey); // 每次新建独立实例,避免引用复用导致的数值覆盖 const archiveItem: ArchiveObject = { referenceTo: selectedObject, objectName: childRel.childSObject, referenceField: childRel.field, isDelete: childRel.cascadeDelete || childRel.restrictedDelete }; result.push(archiveItem); // 仅对需要级联删除的对象递归查询下一级子对象 if (archiveItem.isDelete) { const childResult = this.findChildObjects(childRel.childSObject, globalDescribeMap, visited); result.push(...childResult); } }); return result; }
2. 修改execute方法中的调用逻辑
public async execute(): Promise<void> { const templateExecutionSteps = await this.getTemplateExecutionSteps(); // 获取全局描述 const globalDescribe = await SfDescribe.objects(this.auth); await this.backup.sobjectKeyPrefix(globalDescribe); // 获取用户选中的对象名称 const objectsName = [...templateExecutionSteps.keys()].map(objectName => { const describeObject = globalDescribe.sobjects.find(describeObj => describeObj.name === objectName); if (!describeObject) throw new Error(`Object ${objectName} not found`); if (!describeObject.queryable) throw new Error(`Object ${objectName} not queryable`); return describeObject; }); // 获取组织内所有对象的名称 const globalDescribeNames: string[] = []; globalDescribe.sobjects.forEach(obj => globalDescribeNames.push(obj.name)); // 获取组织内所有对象的全局描述 const describeAllObjects = await SfCompositeUtils.describeObjects(this.auth, globalDescribeNames, true); // 将所有对象的全局描述结果存入Map const globalDescribeMap: Map<string, SFDescribeObjects> = new Map(); describeAllObjects.forEach(objectDescribe => globalDescribeMap.set(objectDescribe.name, objectDescribe)); // 替换原有调用逻辑,避免全局数组污染 this.objectsArray = []; for (const object of objectsName) { const childObjects = this.findChildObjects(object.name, globalDescribeMap); this.objectsArray.push(...childObjects); } }
核心修改说明
- 新增
visited局部集合,第一次访问关联关系就做标记,从根源避免循环递归 - 每次循环都新建独立的
ArchiveObject实例,不会出现引用复用导致的数值覆盖问题 - 递归返回值改为当前层级的查询结果数组,不再复用全局数组,避免重复推送数据
- 全局数组仅在根入口做结果汇总,多个根对象的查询结果互不干扰
- 保留了你原有仅对级联删除/限制删除的对象做深层递归的逻辑,符合需求场景
内容的提问来源于stack exchange,提问作者vgusakov
相关产品推荐
相关产品推荐

