代码之家  ›  专栏  ›  技术社区  ›  Steve Py

Javascript参数类型检查

  •  0
  • Steve Py  · 技术社区  · 7 年前

    我已经用尽我的Google fu来寻找如何在ES6模块方法中最好地断言参数的例子,或者处理可能传递的数据类型的不确定性。即介于字符串和数字类型之间。几个小时以来,我一直在为一个计算属性和MobX的奇怪行为问题而头疼,在这里我有一些测试代码,可以初始化默认发送的值,并且工作正常。计算出的值取一个初始值,从相关数据(贷项和借项)中求出两个总数,然后加上借项并减去贷项以返回调整后的值。所以返回的初始值(1000)+totalDebits(0)-totalCredits(0)返回1000。很简单。但是,当我添加了一个对话框,在列表中输入一个初始值为2000的新项时,我的计算值将返回20000!奇怪的是,我将这些对象保存到本地存储,当从存储的值中刷新时,计算的总数仍然有20000,但是其他现有值具有与它们的初始值相匹配的正确计算值。

    我终于找到了它的原因,并在持久状态下验证了它:输入页面中的initialBalance被存储为“2000”,一个字符串文本2000“+0-0,将0添加到字符串中,同时忽略减号。

    我的问题是,对于普通JS或合适的ES6库,有哪些选项可用于处理可能进入MobX操作等函数的类型?加上处理JS参数时的最佳实践建议。

    例如: 在我的组件中,有一个onChange事件

    onInitialBalanceChange(e){
        const value = e.target.value;
        const account = this.state.account;
        let validationError = null;
        let isBalanceValid = true;
        if(!this.validateInitialBalance(value)){
            validationError = 'The initial balance must be a positive number.'
            isBalanceValid = false;
        } 
        account.updateInitialBalance(value);
        this.setState({validationError: validationError, isBalanceValid: isBalanceValid, isComplete: isBalanceValid && this.state.isNameValid});
    }
    

    updateInitialBalance注册为MobX操作:

    updateInitialBalance(initialBalance){
        if (!!initialBalance) this.initialBalance = initialBalance;
        return this;
    }
    

    我有一个断言,参数不是空的,但是除了一个重要的if()条件外,断言/转换它的类型的最佳实践是什么?initialBalance可以是字符串“2000”或数字2000,它还应该防止无效数据,如“弗莱德”??所以我提出了在duck类型的世界中进行可靠类型检查的问题。

    从我看来,这似乎是可行的,但根据文件来看,这有点“错误”:

    if (!!initialBalance) this.initialBalance = parseInt(initialBalance);
    

    parseInt接受“string”,但是如果initialBalance已经是一个数字(而不是一个数字字符串),那么这似乎也可以工作。

    0 回复  |  直到 7 年前
        1
  •  1
  •   RP-3    7 年前

    听起来这里有三个问题。这些只是我认为应该对每一种观点有所帮助的一些观点。

    如何最好地访问本地存储?

    class LocalStorageWrapper {
    
        setInitialBalanace(balance){
            if(Object.prototype.toString.call(balance) !== '[object Number]') return false;
            // any other validation. > 0? < 9999999? etc
            // set the value
        }
    
        getInitialBalance(){
            const val = // get the value from local storage
            if(!isNaN(parseFloat(val)) && isFinite(val)) return parseInt(val);
            if(Object.prototype.toString.call(val) === '[object Number]') return val;
            // return some default value. The value is either unset or corrupted
        }
    }
    

    笔记:

    • 使用此选项可在整个代码库中访问本地存储,以避免在任何地方重复这些检查。也要对它进行单元测试。
    • Object.prototype.toString.call(balance) !== '[object Number]' 是否IMO是测试有意义数字的最佳方法。见 here 对于测试用例。
    • !isNaN(parseFloat(val)) && isFinite(val) best way 测试可以可靠地解析为数字的“数值”值。

    对于一般验证,我认为抛出一个非常具体的错误并在最后捕获它们通常更安全。例如:

    onInitialBalanceChange(e){
    
        try {
            if(!this.validateInitialBalance(value)){
                throw new Error('initial_balance_negative')
            }
            // other error checks here...
            account.updateInitialBalance(value); // do whatever you want
            this.setState({validationError: null, isBalanceValid: isBalanceValid, isComplete: isBalanceValid && this.state.isNameValid});
        } catch(e) {
            // ideally match against some expected
            if(constantStrings.hasOwnProperty(e.message)){
                this.setState({validationError: constantStrings[e.message]}); // an 'expected' user error
            }else{
                this.setState({validationError: 'Unknown error'}); // a 'true' error
            }
        }
    
    }
    
    // somewhere else in 'strings.js' or something
    const constantStrings = {
        initial_balance_negative: 'The initial balance must be a positive number.'
    };
    

    Duck类型的JS很难