oxc-project/oxc · error

Unexpected comparison of two newly constructed objects

Error message

Unexpected comparison of two newly constructed objects

What it means

Diagnostic from `no-constant-binary-expression`, emitted by `constant_both_always_new` (crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs:103). It fires on loose equality/inequality (`==`/`!=`) where BOTH operands are always-new references — e.g. `[a] == [a]`, `({}) == []`. Because each side constructs a distinct object, even loose equality (which never crosses two object references) is guaranteed false; the help text reads 'These two values can never be equal'.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs:104

    .with_label(span)
}

fn constant_binary_operand(left_or_right: &str, operator: &str, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected constant binary expression")
        .with_help(format!(
            "This compares constantly with the {left_or_right}-hand side of the {operator}"
        ))
        .with_label(span)
}

fn constant_always_new(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected comparison to newly constructed object")
        .with_help("These two values can never be equal")
        .with_label(span)
}

fn constant_both_always_new(span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected comparison of two newly constructed objects")
        .with_help("These two values can never be equal")
        .with_label(span)
}

fn constant_relational_comparison(operator: &str, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn("Unexpected constant relational comparison")
        .with_help(format!("Both sides of the {operator} are literal values"))
        .with_label(span)
}

impl Rule for NoConstantBinaryExpression {
    fn from_configuration(value: serde_json::Value) -> Result<Self, serde_json::error::Error> {
        DefaultRuleConfig::<Self>::from_value(value).map(DefaultRuleConfig::into_inner)
    }

    fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
        match node.kind() {
            AstKind::LogicalExpression(expr) => match expr.operator {

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Use a deep-equality helper: `assert.deepStrictEqual(a, b)` or `isEqual(a, b)`.
  2. Compare stable primitive keys (ids, serialized forms) instead of constructed objects.
  3. Hoist the shared object to a constant and compare references to that constant if identity is meant.
  4. Delete the comparison when both sides are provably the same fresh construction — it always returns false.

Example fix

// before
if ([a, b] == [a, b]) { /* never true */ }

// after
if (a === a && b === b) { }
// or, for real content checks:
if (isEqual(left, right)) { }
Defensive patterns

Strategy: type-guard

Validate before calling

// Gate: loose equality between two constructed literals
const bothFresh = /(\[[^\]]*\]|\{[^}]*\})\s*=={1,2}\s*(\[[^\]]*\]|\{[^}]*\})/.test(src);

Type guard

// Say what you mean: content equality
import { isEqual } from 'lodash-es';
const sameContent = (a, b) => isEqual(a, b);

Prevention

When it happens

Trigger: BinaryExpression with Equality/Inequality where `is_always_new(left) && is_always_new(right)` — two object/array/function/class/regex literals or builtin-global `new` expressions compared with `==`/`!=`, e.g. `JSON.parse(s) == JSON.parse(s)` style copies compared after literal construction like `[a] == [a]`.

Common situations: Array-of-values comparison written as `[x] == [x]`; comparing two configuration object literals for overlap; refactored `deepEqual` sketches that regressed to `==`.

Related errors


AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20). Data as JSON: /api/errors/a52162992a701af0. Report an issue: GitHub.