Meaningless Size Comparison

ID

javascript.meaningless_size_comparison

Severity

high

Remediation Complexity

trivial

Remediation Risk

low

Remediation Effort

low

Resource

Reliability

Language

JavaScript

Tags

reliability, suspicious-comparison

Description

Reports a size or length compared against a bound the value can never cross, which makes the comparison a constant. length on a string or an array, size on a Map or a Set, and a no-argument size() accessor on a collection wrapper all report how many elements there are and none of them can be negative, so >= 0 and > -1 always hold, while < 0 and ⇐ -1 never do.

Rationale

A comparison that cannot change its answer is either a guard that guards nothing or — far more often — an emptiness check that was written with the wrong bound. if (items.length >= 0) reads like "if there is anything in the list" and behaves like "always", so the code inside runs for the empty list too and the bug surfaces later as an undefined property read or an empty report. The mirrored form if (0 > items.length) has the opposite failure: the branch is dead, and whatever it was supposed to handle is silently never handled.

class Cart {
  constructor() {
    this.items = [];
    this.tags = new Set();
  }

  report(data, name) {
    if (this.items.length >= 0) {   // FLAW — always true, the guard does nothing
      console.log(this.items[0].sku);
    }

    if (data.length < 0) {          // FLAW — always false, this branch is dead
      console.log("no data");
    }

    if (this.tags.size < 0) {       // FLAW — a Set size is never negative either
      console.log("no tags");
    }

    if (name.length <= -1) {        // FLAW — always false
      console.log("empty name");
    }

    if (this.items.length > 0) {    // OK — genuine emptiness check
      console.log(this.items[0].sku);
    }

    if (data.length >= 2) {         // OK — a real bound
      console.log(data[1]);
    }
  }
}

Both operand orders are recognised. Only the bounds 0 and -1 are considered — every other bound expresses a real condition.

Matching is by member name on a receiver, so a plain identifier named length is not treated as a size and size() must be called on something. No other member name is accepted: length and size are the only two whose non-negativity holds without knowing the receiver’s type.

Remediation

Decide what the test was meant to say and write that bound. "Has elements" is length > 0 (or length !== 0); "is empty" is length === 0. If the comparison was defensive and the value genuinely cannot be negative, delete it — it adds no protection and draws the reader’s eye away from the checks that do:

if (this.items.length > 0) {
  console.log(this.items[0].sku);
}

if (data.length === 0) {
  console.log("no data");
}

© 2026 Xygeni. All rights reserved