krillgar
krillgar

Reputation: 12805

Unexpected '.' from wrapped expression to chain

I have this method that is calculating a total, and part of it is giving a warning in JS Lint. We're trying to get cleaner JS Lint inspections at work, so I want to see if there's a rational way to get around this that I'm not thinking of.

calculateTotal = function() {
    var hours = parseFloat($hours.val());
    var rate = parserFloat($rate.val());
    var total = '';

    if (!isNaN(hours) && !isNaN(rate)) {
        // This throws the error.
        total = (rate * hours).toFixed(2);
    }

    $total.val(total);
}

I can avoid the message if I do the following:

total = rate * hours;
total = total.toFixed(2);

It's a little too verbose for me to just jump at it, but it may be the best bet.

I checked out this question, and considered doing Number(rate * hours).toFixed(2), but that's (marginally) less performant, plus it would be a bad precedent to start with all of the warnings about using String() as stated in response to the accepted answer there.

This could be moot if my above attempt is the best way to get JS Lint to stop complaining, but I would like to hear from other people.

Upvotes: 4

Views: 124

Answers (1)

ruffin
ruffin

Reputation: 17453

TL;DR

JSLint is going to force you to move the toFixed() from behind of the parentheses. I'd suggest the least annoying place to move it is in the $total.val(total) assignment.

This lints as-is on JSLint.com:

/*jslint white:true, browser:true */
/*global $hours, $rate, $total */

var calculateTotal = function() {
    "use strict";
    var hours = parseFloat($hours.val());
    var rate = parseFloat($rate.val());
    var total;

    if (!isNaN(hours) && !isNaN(rate)) {
        // This throws the error.
        total = rate * hours;
    }

    $total.val(total.toFixed(2));  // moved `toFixed` to here
};

A little longer...

I tried it against the most recent version of JSLint, and it's borking at left_check in JSLint's code, here:

function left_check(left, right) {

// Warn if the left is not one of these:
//      e.b
//      e[b]
//      e()
//      identifier

    var id = left.id;
    if (
        !left.identifier &&
        (
            left.arity !== "binary" ||
            (id !== "." && id !== "(" && id !== "[")
        )
    ) {
        warn("unexpected_a", right);
        return false;
    }
    return true;
}

left is essentially (rate & hours) and right is . with toFixed the next token in this case.

As dangerous as it is to assume code function from comments, I think the comments tell us where JSLint is coming from -- It wants methods called only on objects, not on operations, including type coercions that often occur inside of them. It pretty much has to let you make "fluent" calls, where you chain methods, and the only valid things that can have method calls are...

  • an object: e
  • A property of an object: e.b
  • A property in a collection: e[key]
  • The return value of a function: e()

Just to double check, since your construction used to work in "old JSLint" (the last version before JSLint for ES6), I asked Douglas Crockford. He's pretty terse, but he did confirm JSLint is working as intended.

crockford on paren method

Sorry I can't be more help there. I think there are places where (someExpression).someMethod() is expedient, but understand where JSLint's coming from too. If you're going to have the potential for type coercion, coerce explicitly.

Interesting question; thanks for asking.

Upvotes: 2

Related Questions