如何按OOP模式实现同签名JS验证函数的前置链式调用?
我定义了如下验证函数接口:
validate?: ( rowData: RowData ) => { isValid: boolean; helperText?: string } | string | boolean;
此前我仅为其赋值单个函数,示例如下:
interface ValidationType { isValid: boolean helperText: string } const validateFunction = (data: Data): ValidationType | true => {...} {..., validate = data => validateFunction(data) }
现在我需要在已赋值的验证函数前追加一个默认验证函数,尝试用something.validate = defaultValidation + something.validate实现时,出现错误:Operator '+' cannot be applied to types ...。
我考虑用匿名函数包裹调用两个函数,代码如下:
columns[i].validate = (data: Data): ValidationType | true => { const defaultValidationResponse = defaultValidation(data); if(defaultValidationResponse !== true) return defaultValidationResponse; const answer = columns[i].validate?.(data) || true if (answer === true) { return true } return answer as ValidationType }
请问该实现是否始终有效?同时想了解符合OOP模式的精准实现方式。
一、当前实现的有效性分析
你的这个实现存在致命缺陷:在匿名函数内部调用columns[i].validate?.(data)时,columns[i].validate已经被替换成当前这个匿名函数了,会触发无限递归调用,最终导致栈溢出报错。
举个简单的例子:当你把匿名函数赋值给columns[i].validate后,函数内部再调用这个属性,其实就是调用它自己,循环往复直到栈空间耗尽。
正确的做法是先把原有的验证函数保存下来,再在新函数里调用这个保存的副本,修正后的代码如下:
// 先缓存原验证函数,避免被覆盖后无法调用 const originalValidate = columns[i].validate; columns[i].validate = (data: Data): ValidationType | true => { const defaultValidationResponse = defaultValidation(data); // 默认验证不通过,直接返回结果 if (defaultValidationResponse !== true) return defaultValidationResponse; // 调用缓存的原验证函数,无原函数则直接返回true const answer = originalValidate?.(data) || true; if (answer === true) { return true; } return answer as ValidationType; };
修正后的逻辑是有效的:先执行默认验证,默认验证通过后再执行原自定义验证,只要有一步不通过就返回对应错误结果,全部通过则返回true。
二、符合OOP模式的实现方式
OOP的核心是封装、组合与多态,我们可以把验证逻辑封装成独立的验证器类,通过责任链模式组合多个验证器,实现可扩展的验证逻辑。
1. 定义抽象验证器基类
先统一验证器的接口和链式调用逻辑:
abstract class Validator<T> { // 下一个验证器,形成责任链 private nextValidator?: Validator<T>; // 设置下一个验证器,支持链式调用 public setNext(validator: Validator<T>): Validator<T> { this.nextValidator = validator; return validator; } // 统一的验证入口 public validate(data: T): { isValid: boolean; helperText?: string } | boolean { const result = this.doValidate(data); // 当前验证通过且有后续验证器时,执行下一个验证 if (result === true && this.nextValidator) { return this.nextValidator.validate(data); } return result; } // 子类需实现的具体验证逻辑 protected abstract doValidate(data: T): { isValid: boolean; helperText?: string } | boolean; }
2. 实现具体验证器类
分别实现默认验证和自定义验证的具体逻辑:
// 默认验证器 class DefaultValidator extends Validator<Data> { protected doValidate(data: Data): { isValid: boolean; helperText?: string } | boolean { // 这里写默认验证逻辑,比如检查必填字段 if (!data.id) { return { isValid: false, helperText: "ID不能为空" }; } return true; } } // 自定义验证器 class CustomValidator extends Validator<Data> { protected doValidate(data: Data): { isValid: boolean; helperText?: string } | boolean { // 这里写自定义验证逻辑,比如检查年龄范围 if (data.age < 18) { return { isValid: false, helperText: "年龄必须大于18岁" }; } return true; } }
3. 组合验证器并使用
通过责任链模式组合验证器,再赋值给columns的validate属性:
// 构建验证链:先执行默认验证,再执行自定义验证 const validatorChain = new DefaultValidator(); validatorChain.setNext(new CustomValidator()); // 把验证链绑定到columns的validate属性 columns[i].validate = (data: Data) => validatorChain.validate(data);
这种实现的优势:
- 扩展性强:新增验证逻辑只需新增Validator子类,无需修改原有代码,符合开闭原则。
- 职责单一:每个验证器只负责一类验证逻辑,易于维护和测试。
- 灵活组合:可以通过
setNext任意调整验证顺序和数量,适配不同场景需求。
内容的提问来源于stack exchange,提问作者someRandomSerbianGuy

