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
- Narrow explicitly: check for undefined and throw or return early
- Use optional chaining `?.` and a nullish default `??`
- Write a type-guard function (for example isDefined) and rely on narrowing
- Fix the source API to return `T` when absence is impossible
- 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
- Enable strictNullChecks and fix the source types instead of asserting at call sites
- Reach for `?.`, `??`, or an isDefined guard before ever typing `!`
- Track the repo's remaining `!` count (rg '!' on TS files) and drive it to zero
- Grep new diffs for postfix `!` in review
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Optional chain expressions can return undefined by design: u
- extra non-null assertion
- 'Disallow non-null assertions in the left operand of a nulli
- Confusing combinations of non-null assertion and equal test
- Confusing combinations of non-null assertion and assignment
AI-assisted analysis of oxc-project/oxc@e1e7af627c (2026-08-20).
Data as JSON: /api/errors/d14e3149c6eeff87.
Report an issue: GitHub.