Skip to content

Commit 8c61f9c

Browse files
authored
feat(linter): implement @typescript-eslint/no-non-null-assertion (#3825)
Related issue: #2180 original implementation - code: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/src/rules/no-non-null-assertion.ts - test: https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/eslint-plugin/tests/rules/no-non-null-assertion.test.ts - doc: https://typescript-eslint.io/rules/no-non-null-assertion/ typescript-eslint has a feature that is manually fixable by editor suggestions on this rule, but oxc does not have one as far as I know, so the implementation is simple compared to typescript-eslint
1 parent 5501d5c commit 8c61f9c

File tree

3 files changed

+274
-0
lines changed

3 files changed

+274
-0
lines changed

crates/oxc_linter/src/rules.rs

+2
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ mod typescript {
137137
pub mod no_misused_new;
138138
pub mod no_namespace;
139139
pub mod no_non_null_asserted_optional_chain;
140+
pub mod no_non_null_assertion;
140141
pub mod no_this_alias;
141142
pub mod no_unnecessary_type_constraint;
142143
pub mod no_unsafe_declaration_merging;
@@ -540,6 +541,7 @@ oxc_macros::declare_all_lint_rules! {
540541
typescript::triple_slash_reference,
541542
typescript::prefer_literal_enum_member,
542543
typescript::explicit_function_return_type,
544+
typescript::no_non_null_assertion,
543545
jest::expect_expect,
544546
jest::max_expects,
545547
jest::max_nested_describe,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
use oxc_ast::AstKind;
2+
use oxc_diagnostics::OxcDiagnostic;
3+
use oxc_macros::declare_oxc_lint;
4+
use oxc_span::Span;
5+
6+
use crate::{context::LintContext, rule::Rule, AstNode};
7+
8+
#[derive(Debug, Default, Clone)]
9+
pub struct NoNonNullAssertion;
10+
11+
declare_oxc_lint!(
12+
/// ### What it does
13+
/// Disallow non-null assertions using the ! postfix operator.
14+
///
15+
/// ### Why is this bad?
16+
/// TypeScript's ! non-null assertion operator asserts to the type system that an expression is non-nullable, as in not null or undefined. Using assertions to tell the type system new information is often a sign that code is not fully type-safe. It's generally better to structure program logic so that TypeScript understands when values may be nullable.
17+
///
18+
/// ### Example
19+
/// ```javascript
20+
/// x!;
21+
/// x!.y;
22+
/// x.y!;
23+
/// ```
24+
NoNonNullAssertion,
25+
restriction,
26+
);
27+
28+
fn no_non_null_assertion_diagnostic(span0: Span) -> OxcDiagnostic {
29+
OxcDiagnostic::warn("typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.")
30+
.with_help("Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.")
31+
.with_labels([span0.into()])
32+
}
33+
34+
impl Rule for NoNonNullAssertion {
35+
fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) {
36+
let AstKind::TSNonNullExpression(expr) = node.kind() else { return };
37+
ctx.diagnostic(no_non_null_assertion_diagnostic(expr.span));
38+
}
39+
}
40+
41+
#[test]
42+
fn test() {
43+
use crate::tester::Tester;
44+
45+
let pass = vec!["x;", "x.y;", "x.y.z;", "x?.y.z;", "x?.y?.z;", "!x;"];
46+
47+
let fail = vec![
48+
"x!;",
49+
"x!.y;",
50+
"x.y!;",
51+
"!x!.y;",
52+
"x!.y?.z;",
53+
"x![y];",
54+
"x![y]?.z;",
55+
"x.y.z!();",
56+
"x.y?.z!();",
57+
"x!!!;",
58+
"x!!.y;",
59+
"x.y!!;",
60+
"x.y.z!!();",
61+
"x!?.[y].z;",
62+
"x!?.y.z;",
63+
"x.y.z!?.();",
64+
"
65+
x!
66+
.y
67+
",
68+
"
69+
x!
70+
// comment
71+
.y
72+
",
73+
"
74+
x!
75+
// comment
76+
. /* comment */
77+
y
78+
",
79+
"
80+
x!
81+
// comment
82+
/* comment */ ['y']
83+
",
84+
];
85+
86+
Tester::new(NoNonNullAssertion::NAME, pass, fail).test_and_snapshot();
87+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
---
2+
source: crates/oxc_linter/src/tester.rs
3+
---
4+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
5+
╭─[no_non_null_assertion.tsx:1:1]
6+
1x!;
7+
· ──
8+
╰────
9+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
10+
11+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
12+
╭─[no_non_null_assertion.tsx:1:1]
13+
1x!.y;
14+
· ──
15+
╰────
16+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
17+
18+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
19+
╭─[no_non_null_assertion.tsx:1:1]
20+
1x.y!;
21+
· ────
22+
╰────
23+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
24+
25+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
26+
╭─[no_non_null_assertion.tsx:1:2]
27+
1!x!.y;
28+
· ──
29+
╰────
30+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
31+
32+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
33+
╭─[no_non_null_assertion.tsx:1:1]
34+
1x!.y?.z;
35+
· ──
36+
╰────
37+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
38+
39+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
40+
╭─[no_non_null_assertion.tsx:1:1]
41+
1x![y];
42+
· ──
43+
╰────
44+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
45+
46+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
47+
╭─[no_non_null_assertion.tsx:1:1]
48+
1x![y]?.z;
49+
· ──
50+
╰────
51+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
52+
53+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
54+
╭─[no_non_null_assertion.tsx:1:1]
55+
1x.y.z!();
56+
· ──────
57+
╰────
58+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
59+
60+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
61+
╭─[no_non_null_assertion.tsx:1:1]
62+
1x.y?.z!();
63+
· ───────
64+
╰────
65+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
66+
67+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
68+
╭─[no_non_null_assertion.tsx:1:1]
69+
1x!!!;
70+
· ────
71+
╰────
72+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
73+
74+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
75+
╭─[no_non_null_assertion.tsx:1:1]
76+
1x!!!;
77+
· ───
78+
╰────
79+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
80+
81+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
82+
╭─[no_non_null_assertion.tsx:1:1]
83+
1x!!!;
84+
· ──
85+
╰────
86+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
87+
88+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
89+
╭─[no_non_null_assertion.tsx:1:1]
90+
1x!!.y;
91+
· ───
92+
╰────
93+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
94+
95+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
96+
╭─[no_non_null_assertion.tsx:1:1]
97+
1x!!.y;
98+
· ──
99+
╰────
100+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
101+
102+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
103+
╭─[no_non_null_assertion.tsx:1:1]
104+
1x.y!!;
105+
· ─────
106+
╰────
107+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
108+
109+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
110+
╭─[no_non_null_assertion.tsx:1:1]
111+
1x.y!!;
112+
· ────
113+
╰────
114+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
115+
116+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
117+
╭─[no_non_null_assertion.tsx:1:1]
118+
1x.y.z!!();
119+
· ───────
120+
╰────
121+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
122+
123+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
124+
╭─[no_non_null_assertion.tsx:1:1]
125+
1x.y.z!!();
126+
· ──────
127+
╰────
128+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
129+
130+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
131+
╭─[no_non_null_assertion.tsx:1:1]
132+
1x!?.[y].z;
133+
· ──
134+
╰────
135+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
136+
137+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
138+
╭─[no_non_null_assertion.tsx:1:1]
139+
1x!?.y.z;
140+
· ──
141+
╰────
142+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
143+
144+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
145+
╭─[no_non_null_assertion.tsx:1:1]
146+
1x.y.z!?.();
147+
· ──────
148+
╰────
149+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
150+
151+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
152+
╭─[no_non_null_assertion.tsx:2:10]
153+
1
154+
2x!
155+
· ──
156+
3 │ .y
157+
╰────
158+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
159+
160+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
161+
╭─[no_non_null_assertion.tsx:2:10]
162+
1
163+
2x!
164+
· ──
165+
3// comment
166+
╰────
167+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
168+
169+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
170+
╭─[no_non_null_assertion.tsx:2:10]
171+
1
172+
2x!
173+
· ──
174+
3// comment
175+
╰────
176+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.
177+
178+
typescript-eslint(no-non-null-assertion): Forbidden non-null assertion.
179+
╭─[no_non_null_assertion.tsx:2:10]
180+
1
181+
2x!
182+
· ──
183+
3// comment
184+
╰────
185+
help: Consider using the optional chain operator `?.` instead. This operator includes runtime checks, so it is safer than the compile-only non-null assertion operator.

0 commit comments

Comments
 (0)