简体   繁体   English

JavaScript参数类型检查

[英]Javascript argument type checking

I've been exhausting my Google-fu trying to find examples on how to best assert parameters in ES6 module methods, or deal with the uncertainty of the type of data that may get passed around. 我一直在用我的Google-fu来尝试寻找有关如何在ES6模块方法中最佳断言参数的示例,或处理可能传递的数据类型的不确定性的示例。 Namely between things like strings and numeric types. 即介于字符串和数字类型之间。 I've just been scratching my head for a few hours with a weird behaviour issue around a computed property and MobX where I have some test code that initializes a default sent of values and that works just fine. 我一直在围绕计算属性和MobX出现一个怪异的行为问题,摸索了几个小时,在这里,我有一些测试代码可以初始化默认值的发送,并且工作正常。 The computed value takes an initial value, sums up two totals from related data (credits and debits) then adds the debits and deducts the credits to return the adjusted value. 计算值取一个初始值,从相关数据(贷方和借方)中求出两个总数,然后加上借方并扣除贷方以返回调整后的值。 So the return initialValue (1000) + totalDebits (0) - totalCredits (0) which returns 1000. Simple. 因此,返回的initialValue(1000)+ totalDebits(0)-totalCredits(0)返回1000。简单。 However when I added a dialog to enter a new item to the list with an initial value of 2000, my computed value was coming back as 20000! 但是,当我添加一个对话框以将初始值输入2000到列表中的新项输入时,我的计算值又回到了20000! What was stranger is that I persist these objects to local storage and when I refresh from the stored values the computed total still had 20000, but the other existing values had the correct computed values matching their initial values. 奇怪的是,我将这些对象持久保存到本地存储中,当我从存储的值刷新时,计算出的总数仍为20000,而其他现有值具有与初始值匹配的正确计算值。

I finally did track down the reason for it, and verified it in the persisted state that my initialBalance from the entered page was being stored as "2000", a string literal. 最后,我确实找到了原因,并在持久状态下进行了验证,以确保输入页面中的initialBalance被存储为字符串文字“ 2000”。 "2000" + 0 - 0 which added a 0 to the string, while the minus was ignored. “ 2000” + 0-0向字符串添加了0,而负号被忽略。

My question is what options are there with plain JS or suitable ES6 library for handling types that might be coming into a function such as a MobX action? 我的问题是,普通的JS或合适的ES6库有哪些选项来处理可能会传入诸如MobX action之类的函数的类型? Plus any advice for best practices when dealing with JS arguments in general. 一般而言,在处理JS参数时,还会提供有关最佳做法的任何建议。

For example: In my component I have an onChange event 例如:在我的组件中,我有一个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});
}

the updateInitialBalance is registered as a MobX action: 将updateInitialBalance注册为MobX动作:

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

This is where my question/concern is, and my surprise that I haven't really seen much out there on addressing JS argument types and type conversion beyond explanations of functions that do individual checks. 这就是我的问题/关注的地方,而且令我惊讶的是,我对解决JS参数类型和类型转换方面的了解还不多,除了可以进行单独检查的函数的解释之外。 My component does a validate check on the balance, and could return a balance as number, but that still leaves the update method potentially still being called elsewhere with a numeric string. 我的组件对余额进行验证检查,并且可以将余额作为数字返回,但是仍然使update方法可能仍在其他地方用数字字符串调用。 I have an assertion that the argument isn't null/empty, but what are the best practices for asserting/converting it's type other than a significant # of if() conditions? 我有一个断言,该参数不是null / empty,但是除了有效的if()条件数量以外,断言/转换其类型的最佳实践是什么? initialBalance may be a string "2000" or a number 2000 and it should also guard against invalid data such as "fred"?? initialBalance可以是字符串“ 2000”或数字2000,并且还应防止使用无效数据,例如“ fred”? So I put the question out there for reliable type checking in a duck-typed world. 因此,我提出了一个问题,要求在鸭子式世界中进行可靠的类型检查。

From what I can see this appears to work, but it just feels a bit "wrong" based on the documentation: 从我可以看出,这似乎可行,但是根据文档,感觉有点“错误”:

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

parseInt takes in "string", however if initialBalance is already a number (not a numeric string) this appears to work as well. parseInt接受“字符串”,但是,如果initialBalance已经是一个数字(而不是数字字符串),这似乎也可以工作。

I know this is a bit of an opinionated question, but I'm really looking for options because the screaming in my ears from the C# developer in me is getting deafening. 我知道这是一个自以为是的问题,但是我真的在寻找选择,因为我C#开发人员的声音在耳边嘶哑。 :) :)

It sounds like there are three questions here. 听起来这里有三个问题。 These are just some opinions that I think ought to help with each. 这些只是我认为应该为每个人提供帮助的一些意见。

How should I best access local storage? 我应该如何最好地访问本地存储?

For storing and retrieving values from Local Storage you probably want to write a wrapper module of some kind, that does something like this: 为了从本地存储中存储和检索值,您可能需要编写某种包装模块,该包装模块应执行以下操作:

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
    }
}

Notes: 笔记:

  • Use this to access localstorage throughout your codebase to save you from repeating these checks everywhere. 使用它可以访问整个代码库中的本地存储,从而避免在任何地方重复进行这些检查。 Also unit test the crap out of it. 还要对其中的废品进行单元测试。
  • Object.prototype.toString.call(balance) !== '[object Number]' is IMO the best way to test for a meaningful number. Object.prototype.toString.call(balance) !== '[object Number]'是IMO测试有意义数字的最佳方法。 See here for test cases. 有关测试案例,请参见此处
  • !isNaN(parseFloat(val)) && isFinite(val) is the best way to test for a 'numeric' value that can reliably be parsed to a number. !isNaN(parseFloat(val)) && isFinite(val)是测试可以可靠地解析为数字的“数字”值的最佳方法

Is there a handy pattern to check for user errors 是否有方便的模式来检查用户错误

For generic validation I think it's usually safer to throw a very specific error and catch them it the end. 对于一般验证,我认为通常会比较安全的方法是抛出一个非常具体的错误并最终将其捕获。 For example: 例如:

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-typed JS is hard 鸭式JS很难

I know :(. But I strongly recommend Typescript. The learning curve really isn't too steep and if it's an option for you project, use it. It'll catch plenty of errors for you ahead of time (including the local-storage-is-a-string-value-store bug) and save all the trouble unit testing these things. 我知道:(。但是我强烈建议Typescript。学习曲线的确不是太陡峭,如果您可以选择项目,请使用它。它会为您提前捕获很多错误(包括本地存储) -is-a-string-value-store bug),并保存所有测试这些问题的麻烦单元。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM