{"record":{"id":"252677f4fc7afe7e","repo":"oxc-project/oxc","slug":"confusing-combination-of-non-null-assertion-and","errorCode":null,"errorMessage":"Confusing combination of non-null assertion and `{op_str}` operator like `a! {op_str} b`, which might be misinterpreted as `!(a {op_str} b)`.","messagePattern":"Confusing combination of non-null assertion and `(.+?)` operator like `a! (.+?) b`, which might be misinterpreted as `!\\(a (.+?) b\\)`\\.","errorType":"validation","errorClass":"OxcDiagnostic","httpStatus":null,"severity":"warning","filePath":"crates/oxc_linter/src/rules/typescript/no_confusing_non_null_assertion.rs","lineNumber":84,"sourceCode":"    OxcDiagnostic::warn(format!(\n        r\"Confusing combinations of non-null assertion and equal test like `a! {op_str} b`, which looks very similar to not equal `a !{op_str} b`.\"\n    ))\n    .with_help(\n        r\"Wrap left-hand side in parentheses to avoid putting non-null assertion `!` and `=` together.\",\n    )\n    .with_label(span)\n}\n\nfn confusing_non_null_assignment_assertion_diagnostic(op_str: &str, span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\n        r\"Confusing combinations of non-null assertion and assignment like `a! {op_str} b`, which looks very similar to not equal `a !{op_str} b`.\"\n    ))\n    .with_help(r\"Remove the `!`, or wrap the left-hand side in parentheses.\")\n    .with_label(span)\n}\n\nfn confusing_non_null_operator_diagnostic(op_str: &str, span: Span) -> OxcDiagnostic {\n    OxcDiagnostic::warn(format!(\n        \"Confusing combination of non-null assertion and `{op_str}` operator like `a! {op_str} b`, which might be misinterpreted as `!(a {op_str} b)`.\"\n    ))\n    .with_help(\"Remove the `!`, or wrap the left-hand side in parentheses.\")\n    .with_label(span)\n}\n\nfn get_depth_ends_in_bang(expr: &Expression<'_>) -> Option<u32> {\n    match expr {\n        Expression::TSNonNullExpression(_) => Some(0),\n        Expression::ChainExpression(chain_expr) => {\n            matches!(&chain_expr.expression, ChainElement::TSNonNullExpression(_)).then_some(0)\n        }\n        Expression::BinaryExpression(binary_expr) => {\n            get_depth_ends_in_bang(&binary_expr.right).map(|x| x + 1)\n        }\n        Expression::UnaryExpression(unary_expr) => {\n            get_depth_ends_in_bang(&unary_expr.argument).map(|x| x + 1)\n        }","sourceCodeStart":66,"sourceCodeEnd":102,"githubUrl":"https://github.com/oxc-project/oxc/blob/36ec0ef2bae567d1a8413ada2fe795f1af1f2785/crates/oxc_linter/src/rules/typescript/no_confusing_non_null_assertion.rs#L66-L102","documentation":"Operator variant of typescript/no-confusing-non-null-assertion: the left operand of `in` or `instanceof` is non-null asserted, as in `a! in obj` or `a! instanceof C`. The text can be misread as the negation `!(a in obj)`, and the precedence of `!` here is assertion, not logical not, which is the trap.","triggerScenarios":"Any BinaryExpression with operator `in` or `instanceof` whose left side ends in `!`, e.g. `key! in cache`, `err! instanceof TypeError`; the run() special-cases these two operators separately from the equality variants.","commonSituations":"Type-guard-style code checking keys or error types after an assertion; developers intending `!(a in obj)` but writing `a! in obj`; ported JS code that used `!` as logical not before these operators.","solutions":["Remove the `!` if the operand cannot be null/undefined: `key in cache`","If you meant logical negation, write it explicitly: `!(key in cache)`","If the assertion is intentional, wrap the operand: `(key!) in cache`"],"exampleFix":"// before\nif (key! in cache) {}\n\n// after\nif (key in cache) {}\n// or, if negation was intended:\nif (!(key in cache)) {}","handlingStrategy":"type-guard","validationCode":"// .oxlintrc.json\n{\n  \"rules\": { \"typescript/no-confusing-non-null-assertion\": \"warn\" }\n}","typeGuard":"function isNonEmptyString(v: string | null | undefined): v is string {\n  return typeof v === 'string' && v.length > 0;\n}\n\n// instead of `key! in cache`, narrow or drop the assertion\nif (isNonEmptyString(key) && key in cache) { /* ... */ }\n// negation must be written explicitly:\nif (!(key in cache)) { /* ... */ }","tryCatchPattern":null,"preventionTips":["Never write `!` directly before `in` or `instanceof`; read it as logical-not and double-check intent","Keep candidate keys typed as non-nullable so the assertion is unnecessary","Use Set.has / Map.has for membership tests where `in` on objects invites this pattern"],"tags":["typescript","lint","non-null-assertion","operators","oxlint"],"backgroundTag":"non-null-assertion-confusion","analyzedSha":"36ec0ef2bae567d1a8413ada2fe795f1af1f2785","analyzedAt":"2026-08-20T07:01:07.079Z","contentChangedAt":"2026-08-20T07:01:07.079Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}