Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,26 @@ tags: None

code: "typescript-eslint(no-non-null-asserted-optional-chain)"
code_description.href: "https://oxc.rs/docs/guide/usage/linter/rules/typescript_eslint/no-non-null-asserted-optional-chain"
message: "non-null assertions after an optional chain expression\nhelp: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion."
range: Range { start: Position { line: 11, character: 18 }, end: Position { line: 11, character: 18 } }
related_information[0].message: ""
message: "Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.\nhelp: Remove the non-null assertion."
range: Range { start: Position { line: 11, character: 18 }, end: Position { line: 11, character: 19 } }
related_information[0].message: "non-null assertion made after optional chain"
related_information[0].location.uri: "file://<variable>/fixtures/linter/issue_9958/issue.ts"
related_information[0].location.range: Range { start: Position { line: 11, character: 18 }, end: Position { line: 11, character: 18 } }
related_information[1].message: ""
related_information[0].location.range: Range { start: Position { line: 11, character: 21 }, end: Position { line: 11, character: 22 } }
related_information[1].message: "optional chain used"
related_information[1].location.uri: "file://<variable>/fixtures/linter/issue_9958/issue.ts"
related_information[1].location.range: Range { start: Position { line: 11, character: 21 }, end: Position { line: 11, character: 21 } }
related_information[1].location.range: Range { start: Position { line: 11, character: 18 }, end: Position { line: 11, character: 19 } }
severity: Some(Error)
source: Some("oxc")
tags: None


code: "None"
code_description.href: "None"
message: "non-null assertion made after optional chain"
range: Range { start: Position { line: 11, character: 21 }, end: Position { line: 11, character: 22 } }
related_information[0].message: "original diagnostic"
related_information[0].location.uri: "file://<variable>/fixtures/linter/issue_9958/issue.ts"
related_information[0].location.range: Range { start: Position { line: 11, character: 18 }, end: Position { line: 11, character: 19 } }
severity: Some(Hint)
source: Some("oxc")
tags: None
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ use crate::{
rule::Rule,
};

fn no_non_null_asserted_optional_chain_diagnostic(span: Span, span1: Span) -> OxcDiagnostic {
OxcDiagnostic::warn("non-null assertions after an optional chain expression")
.with_help("Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.")
.with_labels([span, span1])
fn no_non_null_asserted_optional_chain_diagnostic(
chain_span: Span,
assertion_span: Span,
) -> OxcDiagnostic {
OxcDiagnostic::warn("Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.")
.with_help("Remove the non-null assertion.")
.with_label(assertion_span.primary_label("non-null assertion made after optional chain"))
.and_label(chain_span.label("optional chain used"))
}

#[derive(Debug, Default, Clone)]
Expand All @@ -27,20 +31,35 @@ declare_oxc_lint!(
/// Disallow non-null assertions after an optional chain expression.
///
/// ### Why is this bad?
/// `?.` optional chain expressions provide undefined if an object is null or undefined.
/// Using a `!` non-null assertion to assert the result of an `?.` optional chain expression is non-nullable is likely wrong.
///
/// Most of the time, either the object was not nullable and did not need the `?.` for its property lookup, or the `!` is incorrect and introducing a type safety hole.
/// By design, optional chain expressions (`?.`) provide `undefined` as the expression's value, if the object being
/// accessed is `null` or `undefined`, instead of throwing an error. Using a non-null assertion (`!`) to assert the
/// result of an optional chain expression is contradictory and likely wrong, as it indicates the code is both expecting
/// the value to be potentially `null` or `undefined` and non-null at the same time.
///
/// In most cases, either:
/// 1. The object is not nullable and did not need the `?.` for its property lookup
/// 2. The non-null assertion is incorrect and introduces a type safety hole.
///
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
///
/// ### Example
/// ```ts
/// foo?.bar!;
/// foo?.bar()!;
/// ```
///
/// Examples of **correct** code for this rule:
///
/// ```ts
/// foo?.bar;
/// foo.bar!;
/// ```
NoNonNullAssertedOptionalChain,
typescript,
correctness,
pending
fix
);

impl Rule for NoNonNullAssertedOptionalChain {
Expand Down Expand Up @@ -90,10 +109,14 @@ impl Rule for NoNonNullAssertedOptionalChain {
if let Some(chain_span) = chain_span {
let chain_span_end = chain_span.end;
let non_null_end = non_null_expr.span.end - 1;
ctx.diagnostic(no_non_null_asserted_optional_chain_diagnostic(
Span::new(chain_span_end, chain_span_end),
Span::new(non_null_end, non_null_end),
));
let diagnostic = no_non_null_asserted_optional_chain_diagnostic(
Span::sized(chain_span_end, 1),
Span::sized(non_null_end, 1),
);
// ctx.diagnostic(diagnostic);
ctx.diagnostic_with_fix(diagnostic, |fixer| {
fixer.delete_range(Span::sized(non_null_end, 1))
});
}
}

Expand Down Expand Up @@ -143,11 +166,24 @@ fn test() {
"(foo?.bar!)()",
];

let fix = vec![
("foo?.bar!", "foo?.bar"),
("foo?.['bar']!", "foo?.['bar']"),
("foo?.bar()!", "foo?.bar()"),
("(foo?.bar)!.baz", "(foo?.bar).baz"),
("(foo?.bar)!().baz", "(foo?.bar)().baz"),
("(foo?.bar)!", "(foo?.bar)"),
("(foo?.bar)!()", "(foo?.bar)()"),
("(foo?.bar!)", "(foo?.bar)"),
("(foo?.bar!)()", "(foo?.bar)()"),
];

Tester::new(
NoNonNullAssertedOptionalChain::NAME,
NoNonNullAssertedOptionalChain::PLUGIN,
pass,
fail,
)
.expect_fix(fix)
.test_and_snapshot();
}
Original file line number Diff line number Diff line change
@@ -1,72 +1,92 @@
---
source: crates/oxc_linter/src/tester.rs
---
⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:4]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:9]
1 │ foo?.bar!;
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:4]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:13]
1 │ foo?.['bar']!;
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:4]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:11]
1 │ foo?.bar()!;
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:8]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:12]
1 │ foo.bar?.()!;
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:5]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:11]
1 │ (foo?.bar)!.baz
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:5]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:11]
1 │ (foo?.bar)!().baz
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:5]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:11]
1 │ (foo?.bar)!
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:5]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:11]
1 │ (foo?.bar)!()
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:5]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:10]
1 │ (foo?.bar!)
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.

⚠ typescript-eslint(no-non-null-asserted-optional-chain): non-null assertions after an optional chain expression
╭─[no_non_null_asserted_optional_chain.tsx:1:5]
⚠ typescript-eslint(no-non-null-asserted-optional-chain): Optional chain expressions can return undefined by design: using a non-null assertion is unsafe and wrong.
╭─[no_non_null_asserted_optional_chain.tsx:1:10]
1 │ (foo?.bar!)()
· ▲ ▲
· ┬ ┬
· │ ╰── non-null assertion made after optional chain
· ╰── optional chain used
╰────
help: Optional chain expressions can return undefined by design - using a non-null assertion is unsafe and wrong. You should remove the non-null assertion.
help: Remove the non-null assertion.