oxc-project/oxc · warning · OxcDiagnostic

Forbidden non-null assertion.

Error message

Forbidden non-null assertion.

What it means

Oxlint's no-non-null-assertion bans the postfix `!` operator outright; it is a restriction rule still marked pending in the declare_oxc_lint block at no_non_null_assertion.rs:41-47. The note explains that `!` removes null and undefined from the type, and when the asserted expression feeds a member access the help at lines 53-56 recommends `?.` instead, spelling out the runtime difference.

Source

Thrown at crates/oxc_linter/src/rules/typescript/no_non_null_assertion.rs:48

    /// x.y!;
    /// ```
    ///
    /// Examples of **correct** code for this rule:
    /// ```ts
    /// x;
    /// x?.y;
    /// x.y;
    /// ```
    NoNonNullAssertion,
    typescript,
    restriction,
    pending,
    version = "0.5.0",
    short_description = "Disallow non-null assertions using the `!` postfix operator.",
);

fn no_non_null_assertion_diagnostic(span: Span, is_member_expression: bool) -> OxcDiagnostic {
    let diagnostic = OxcDiagnostic::warn("Forbidden non-null assertion.")
        .with_note(
            "The non-null assertion operator (`!`) removes `null` and `undefined` from the type. For example, it changes `number | undefined` to `number`.",
        )
        .with_label(span);

    if is_member_expression {
        diagnostic.with_help("Consider using the optional chain operator `?.` instead. `x!.y` is equivalent to `x.y` at runtime and will throw if `x` is `null` or `undefined`, but `x?.y` will return `undefined`.")
    } else {
        diagnostic
    }
}

impl Rule for NoNonNullAssertion {
    fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
        let AstKind::TSNonNullExpression(expr) = node.kind() else { return };
        let is_member_expression = ctx.nodes().parent_kind(node.id()).is_member_expression_kind();
        ctx.diagnostic(no_non_null_assertion_diagnostic(expr.span, is_member_expression));
    }

View on GitHub (pinned to e1e7af627c)

Solutions

  1. Narrow explicitly: check for undefined and throw or return early
  2. Use optional chaining `?.` and a nullish default `??`
  3. Write a type-guard function (for example isDefined) and rely on narrowing
  4. Fix the source API to return `T` when absence is impossible
  5. Last resort: `// oxlint-disable-next-line typescript/no-non-null-assertion`

Example fix

// before
const user = users.find(u => u.id === id)!;

// after
const user = users.find(u => u.id === id);
if (!user) throw new Error(`user ${id} not found`);
Defensive patterns

Strategy: validation

Validate before calling

npx oxlint --deny-warnings .

Type guard

function isDefined<T>(value: T | null | undefined): value is T {
  return value !== null && value !== undefined;
}

// usage instead of `users.find(u => u.id === id)!`
const found = users.find(u => u.id === id);
if (!isDefined(found)) throw new Error('user not found');

Prevention

When it happens

Trigger: Any `expr!`: `foo!.bar`, `users.find(u => u.id === id)!`, `value!.toString()` - the rule flags every occurrence of the operator.

Common situations: Teams that adopted a zero-assertion policy; code against APIs returning `T | undefined` where developers assert instead of narrowing; adoption of this rule in an existing codebase produces a burst of these.

Understand the failure class

Related errors


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