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

如何按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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 19:15:50