oxc-project/oxc · warning · OxcDiagnostic

Unexpected negation of the left operand of '{operator}' oper

Error message

Unexpected negation of the left operand of '{operator}' operator.

What it means

Diagnostic from oxlint's no-unsafe-negation rule. Unary ! binds more tightly than the in and instanceof operators (and <, >, <=, >= when enforceForOrderingRelations is on), so !a in b computes (!a) in b, which is almost never intended. The {operator} placeholder names the offended operator; the fix is parentheses.

Source

Thrown at crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs:17

use oxc_ast::{AstKind, ast::Expression};
use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint;
use oxc_span::{GetSpan, Span};
use oxc_syntax::operator::UnaryOperator;
use schemars::JsonSchema;
use serde::Deserialize;

use crate::{
    AstNode,
    context::LintContext,
    fixer::RuleFixer,
    rule::{DefaultRuleConfig, Rule},
};

fn no_unsafe_negation_diagnostic(operator: &str, span: Span) -> OxcDiagnostic {
    OxcDiagnostic::warn(format!(
        "Unexpected negation of the left operand of '{operator}' operator."
    ))
    .with_help(format!(
        "Use `()` to negate the whole expression, as '!' binds more closely than '{operator}'"
    ))
    .with_label(span)
}

#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]
#[serde(rename_all = "camelCase", default, deny_unknown_fields)]
pub struct NoUnsafeNegation {
    /// The `enforceForOrderingRelations` option determines whether negation is allowed
    /// on the left-hand side of ordering relational operators (<, >, <=, >=).
    ///
    /// The purpose is to avoid expressions such as `!a < b` (which is equivalent to `(a ? 0 : 1) < b`)
    /// when what is really intended is `!(a < b)`.
    enforce_for_ordering_relations: bool,
}

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Wrap the whole comparison in parentheses: !(key in object), !(obj instanceof Ctor).
  2. For property membership prefer Object.hasOwn(obj, key) or !Object.hasOwn(...).
  3. Enable "enforceForOrderingRelations": true in the rule config to also cover <, >, <=, >=.
  4. For TS projects rely on or align with the compiler check instead of duplicating suppressions.

Example fix

// before
if (!key in config) { /* ... */ }
// after
if (!(key in config)) { /* ... */ }
Defensive patterns

Strategy: validation

Validate before calling

ast-grep --lang js -p '!$A in $B' -p '!$A instanceof $B'

Prevention

When it happens

Trigger: if (!key in object) {}; if (!obj instanceof Ctor) {}; with the option enabled also !a < b (equivalent to (a ? 0 : 1) < b). The rule fires on a UnaryExpression ! as the left operand of these binary operators.

Common situations: Writing 'key is not in map' as !key in map; porting membership checks from other languages; TypeScript users may disable the rule since tsc also flags it.

Related errors


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