{"record":{"id":"0643509b86f6ee64","repo":"oxc-project/oxc","slug":"unexpected-negation-of-the-left-operand-of-opera","errorCode":null,"errorMessage":"Unexpected negation of the left operand of '{operator}' operator.","messagePattern":"Unexpected negation of the left operand of '(.+?)' operator\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs","lineNumber":17,"sourceCode":"use oxc_ast::{AstKind, ast::Expression};\nuse oxc_diagnostics::OxcDiagnostic;\nuse oxc_macros::declare_oxc_lint;\nuse oxc_span::{GetSpan, Span};\nuse oxc_syntax::operator::UnaryOperator;\nuse schemars::JsonSchema;\nuse serde::Deserialize;\n\nuse crate::{\n    AstNode,\n    context::LintContext,\n    fixer::RuleFixer,\n    rule::{DefaultRuleConfig, Rule},\n};\n\nfn no_unsafe_negation_diagnostic(operator: &str, span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\n        \"Unexpected negation of the left operand of '{operator}' operator.\"\n    ))\n    .with_help(format!(\n        \"Use `()` to negate the whole expression, as '!' binds more closely than '{operator}'\"\n    ))\n    .with_label(span)\n}\n\n#[derive(Debug, Default, Clone, JsonSchema, Deserialize)]\n#[serde(rename_all = \"camelCase\", default, deny_unknown_fields)]\npub struct NoUnsafeNegation {\n    /// The `enforceForOrderingRelations` option determines whether negation is allowed\n    /// on the left-hand side of ordering relational operators (<, >, <=, >=).\n    ///\n    /// The purpose is to avoid expressions such as `!a < b` (which is equivalent to `(a ? 0 : 1) < b`)\n    /// when what is really intended is `!(a < b)`.\n    enforce_for_ordering_relations: bool,\n}","sourceCodeStart":1,"sourceCodeEnd":35,"githubUrl":"https://github.com/oxc-project/oxc/blob/e1e7af627c8843ab64044ed466b128fcc21a035b/crates/oxc_linter/src/rules/eslint/no_unsafe_negation.rs#L1-L35","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Wrap the whole comparison in parentheses: !(key in object), !(obj instanceof Ctor).","For property membership prefer Object.hasOwn(obj, key) or !Object.hasOwn(...).","Enable \"enforceForOrderingRelations\": true in the rule config to also cover <, >, <=, >=.","For TS projects rely on or align with the compiler check instead of duplicating suppressions."],"exampleFix":"// before\nif (!key in config) { /* ... */ }\n// after\nif (!(key in config)) { /* ... */ }","handlingStrategy":"validation","validationCode":"ast-grep --lang js -p '!$A in $B' -p '!$A instanceof $B'","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Code-review any in/instanceof whose left operand starts with '!' and add parentheses.","Enable \"enforceForOrderingRelations\": true to also catch !a < b forms.","Prefer Object.hasOwn or Map.has for membership checks, which avoid the precedence trap entirely."],"tags":["eslint","oxlint","operators","precedence","in-operator","instanceof"],"backgroundTag":"negation-operator-precedence","analyzedSha":"e1e7af627c8843ab64044ed466b128fcc21a035b","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}