Detecting Type Issues in JavaScript

by pierre yves nicolas|

    JavaScript is very flexible and tries as much as possible to run code without raising an error. This is both a blessing and a curse. It’s a blessing for beginners who don’t understand everything they’re doing. It may become a curse when a subtle coding mistake leads to strange behavior instead of causing a clear failure.

    Some of these coding mistakes are related to types. A JavaScript variable doesn’t have a defined type and its type can change during the lifetime of the variable. That’s a powerful feature, but it also makes it quite easy to make mistakes about types. The good news is that SonarJS is now able to detect some of these issues!

    Let’s look at an example. It makes no sense to use a strict equality operator like === or !== on two operands which don’t have the same type: in such cases, === always returns false and !== always returns true. We have a rule to check that and this rule found the following issue in JQuery:

    In this case, we know that “type” is either a string or undefined when it is compared to the boolean value false with a strict equality operator. This condition is therefore useless, and such a comparison is certainly a bug.

    Of course, we can go further. SonarJS embeds some knowledge about built-in objects and their properties and methods. We added a new rule “Non-existent properties shouldn't be accessed for reading” which is based on that knowledge. It detects issues which could be due to a typo in the name of the property or to a mistake about the type of the variable, such as the following issue which was found in the OpenCart project:

    This piece of code confuses two of its variables: “number” and “s”. The first one is a number and the second is a string representation of the first. The “length” property therefore exists on “s”, but is undefined on “number”. As a result, this function does not return what it’s supposed to.

    Confusion about variable types can also be revealed by another rule: “Values not convertible to numbers should not be used in numeric comparisons”. The fact that an operand cannot be converted to a number could go unnoticed because of JavaScript’s flexibility: the operand would simply be converted to NaN, and the comparison would return false. This rule should help to spot such mistakes. Here’s an example that was detected in the Dojo Toolkit extras library:

    We know that “methodArgs” may be an array: when it is, comparing it to a number doesn’t make sense and that’s what the rule detects. The author of this code probably intended to use methodArgs.length in the comparison.

    How can SonarJS catch such mistakes? Briefly, we rely on path-sensitive dataflow analysis: as we explained a few months ago, our analyzer can explore the various execution paths of a function and the possible constraints on the variables. In the last few versions, we improved our engine so that it tracks the types of the variables. We derive type information based on indicators in the code such as:

    • Literals, e.g. 42 is a number, [] is an array.
    • Operators, e.g. the result of a + can be either a number (addition) or a string (concatenation).
    • typeof expressions.
    • Calls to built-in functions, e.g. we know that a call to Number.isNaN returns a boolean value.

    That not only allowed us to implement the rules I just described, it also improved existing rules not directly related to types. The rule which checks for conditions which are always true or false is now able to find new issues such as the following one in the YUI project:

    This piece of code tests whether “config” is a function twice. However, it’s re-assigned to null if the first test returns true. We therefore know for sure that the second test will return false. This rule doesn’t specifically check the types of the variables, but it is based on all the constraints we’ve derived on the variables and type is one of them.

    Detecting such issues can greatly help JavaScript developers. Try it! Two options are available. The first one is to use a SonarQube server, either your own or the detected issues will look similar to the screenshots above. The other option is to use SonarLint inside your IDE: you can then detect issues as you code. Of course, you can use both a SonarQube server and SonarLint, either way you could save hours of debugging time!