如何重构含多||条件的数组遍历TypeScript函数?
重构思路与优化方案
针对你这个TypeScript函数,这里提供几个实用的重构方向,既提升代码可读性和维护性,也能优化执行效率:
1. 先明确类型,移除any
原函数大量使用any类型,完全丢失了TypeScript的类型优势。先定义清晰的接口和枚举,让代码更安全:
// 先明确枚举(如果项目里已有,直接复用即可) enum CallTimingType { SpecificDates = 'SpecificDates', AnyTime = 'AnyTime', Monthly = 'Monthly', OnPaymentDates = 'OnPaymentDates', AnyInterestAdjustmentDate = 'AnyInterestAdjustmentDate' } // 定义调用计划项的接口 interface CallScheduleItem { CallTimingType: CallTimingType; } // 定义security的接口 interface Security { IsCallable: boolean; } // 定义返回值的接口 interface CallInfo { CallTimingType: 'Not Callable' | 'Discrete' | 'Continuous'; isCheckMark: boolean | null; }
2. 用映射表简化条件判断
原函数的多分支if判断冗余且难维护,用映射表将枚举值和结果类型直接关联,后续新增枚举值只需修改映射表:
版本一:保留原遍历逻辑(最后一个匹配项生效)
getCallInfo = (callSch: { Calls: CallScheduleItem[] }, security: Security): CallInfo => { const defaultCallInfo: CallInfo = { CallTimingType: 'Not Callable', isCheckMark: null, }; if (!security.IsCallable) return defaultCallInfo; // 枚举到结果类型的映射表 const timingTypeMap: Record<CallTimingType, 'Discrete' | 'Continuous'> = { [CallTimingType.SpecificDates]: 'Discrete', [CallTimingType.AnyTime]: 'Continuous', [CallTimingType.Monthly]: 'Continuous', [CallTimingType.OnPaymentDates]: 'Continuous', [CallTimingType.AnyInterestAdjustmentDate]: 'Continuous' }; const callInfo = { ...defaultCallInfo }; callSch.Calls.forEach(el => { const mappedType = timingTypeMap[el.CallTimingType]; if (mappedType) { callInfo.CallTimingType = mappedType; callInfo.isCheckMark = true; } }); return callInfo; };
版本二:提前终止遍历(优化性能)
原函数会遍历所有Calls元素,但实际上如果遇到Continuous类型的项,后续遍历不会改变最终结果(因为Continuous会覆盖Discrete)。可以提前终止遍历,提升执行效率:
getCallInfo = (callSch: { Calls: CallScheduleItem[] }, security: Security): CallInfo => { const defaultCallInfo: CallInfo = { CallTimingType: 'Not Callable', isCheckMark: null, }; if (!security.IsCallable) return defaultCallInfo; const callInfo = { ...defaultCallInfo }; let foundContinuous = false; // 优先查找Continuous类型,找到就终止遍历 for (const el of callSch.Calls) { if ([ CallTimingType.AnyTime, CallTimingType.Monthly, CallTimingType.OnPaymentDates, CallTimingType.AnyInterestAdjustmentDate ].includes(el.CallTimingType)) { callInfo.CallTimingType = 'Continuous'; callInfo.isCheckMark = true; foundContinuous = true; break; } } // 没找到Continuous,再检查是否有Discrete类型 if (!foundContinuous) { const hasDiscrete = callSch.Calls.some(el => el.CallTimingType === CallTimingType.SpecificDates); if (hasDiscrete) { callInfo.CallTimingType = 'Discrete'; callInfo.isCheckMark = true; } } return callInfo; };
版本三:用reduce简化遍历逻辑
如果喜欢函数式风格,可以用reduce一次性完成遍历和结果累积:
getCallInfo = (callSch: { Calls: CallScheduleItem[] }, security: Security): CallInfo => { const defaultCallInfo: CallInfo = { CallTimingType: 'Not Callable', isCheckMark: null, }; if (!security.IsCallable) return defaultCallInfo; const timingTypeMap: Record<CallTimingType, 'Discrete' | 'Continuous'> = { [CallTimingType.SpecificDates]: 'Discrete', [CallTimingType.AnyTime]: 'Continuous', [CallTimingType.Monthly]: 'Continuous', [CallTimingType.OnPaymentDates]: 'Continuous', [CallTimingType.AnyInterestAdjustmentDate]: 'Continuous' }; return callSch.Calls.reduce((acc, el) => { const mappedType = timingTypeMap[el.CallTimingType]; return mappedType ? { ...acc, CallTimingType: mappedType, isCheckMark: true } : acc; }, defaultCallInfo); };
重构核心要点
- 类型安全:移除
any,用接口和枚举明确类型,减少潜在bug,提升代码可读性。 - 逻辑简化:映射表替代多分支
if,后续维护只需修改映射表,降低代码复杂度。 - 性能优化:如果业务允许,提前终止遍历,避免不必要的循环。
- 无副作用:通过对象展开创建新对象,避免直接修改初始对象,让逻辑更纯净。
内容的提问来源于stack exchange,提问作者chewie
相关产品推荐
相关产品推荐

