Skip to content

Commit e963d3b

Browse files
committed
feat(linter/strict-boolean-expression): add rule
1 parent 010c88f commit e963d3b

File tree

5 files changed

+161
-0
lines changed

5 files changed

+161
-0
lines changed

apps/oxlint/fixtures/tsgolint/.oxlintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
"typescript/restrict-plus-operands": "error",
4040
"typescript/restrict-template-expressions": "error",
4141
"typescript/return-await": "error",
42+
"typescript/strict-boolean-expressions": "error",
4243
"typescript/switch-exhaustiveness-check": "error",
4344
"typescript/unbound-method": "error",
4445
"typescript/use-unknown-in-catch-callback-variable": "error",
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Examples of incorrect code for strict-boolean-expressions rule
2+
3+
// Non-boolean values in conditions
4+
const str = 'hello';
5+
if (str) { // string is not a boolean
6+
console.log('string');
7+
}
8+
9+
const num = 42;
10+
if (num) { // number is not a boolean
11+
console.log('number');
12+
}
13+
14+
const obj = { foo: 'bar' };
15+
if (obj) { // object is not a boolean
16+
console.log('object');
17+
}
18+
19+
// Nullable booleans
20+
declare const maybeBool: boolean | null;
21+
if (maybeBool) { // nullable boolean
22+
console.log('maybe');
23+
}
24+
25+
// Undefined checks
26+
declare const maybeString: string | undefined;
27+
if (maybeString) { // should explicitly check !== undefined
28+
console.log(maybeString);
29+
}
30+
31+
// Logical operators with non-booleans
32+
const result1 = str && num;
33+
const result2 = obj || num;
34+
35+
// While loops with non-boolean
36+
while (num) {
37+
console.log('loop');
38+
break;
39+
}
40+
41+
// Do-while with non-boolean
42+
do {
43+
console.log('do');
44+
} while (str);
45+
46+
// For loop with non-boolean
47+
for (let i = 0; i; i++) {
48+
console.log('for');
49+
}
50+
51+
// Ternary with non-boolean
52+
const ternary = str ? 'yes' : 'no';
53+
54+
// Logical NOT on non-boolean
55+
const negated = !str;
56+
57+
// Mixed types in logical expressions
58+
declare const mixed: string | number;
59+
if (mixed) {
60+
console.log('mixed');
61+
}
62+
63+
// any type (should allow or warn depending on config)
64+
declare const anyValue: any;
65+
if (anyValue) {
66+
console.log('any');
67+
}

crates/oxc_linter/src/generated/rule_runner_impls.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2943,6 +2943,11 @@ impl RuleRunner for crate::rules::typescript::return_await::ReturnAwait {
29432943
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
29442944
}
29452945

2946+
impl RuleRunner for crate::rules::typescript::strict_boolean_expressions::StrictBooleanExpressions {
2947+
const NODE_TYPES: Option<&AstTypesBitset> = None;
2948+
const RUN_FUNCTIONS: RuleRunFunctionsImplemented = RuleRunFunctionsImplemented::Unknown;
2949+
}
2950+
29462951
impl RuleRunner
29472952
for crate::rules::typescript::switch_exhaustiveness_check::SwitchExhaustivenessCheck
29482953
{

crates/oxc_linter/src/rules.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ pub(crate) mod typescript {
278278
pub mod restrict_plus_operands;
279279
pub mod restrict_template_expressions;
280280
pub mod return_await;
281+
pub mod strict_boolean_expressions;
281282
pub mod switch_exhaustiveness_check;
282283
pub mod triple_slash_reference;
283284
pub mod unbound_method;
@@ -1136,6 +1137,7 @@ oxc_macros::declare_all_lint_rules! {
11361137
typescript::restrict_plus_operands,
11371138
typescript::restrict_template_expressions,
11381139
typescript::return_await,
1140+
typescript::strict_boolean_expressions,
11391141
typescript::switch_exhaustiveness_check,
11401142
typescript::triple_slash_reference,
11411143
typescript::unbound_method,
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
use oxc_macros::declare_oxc_lint;
2+
3+
use crate::rule::Rule;
4+
5+
#[derive(Debug, Default, Clone)]
6+
pub struct StrictBooleanExpressions;
7+
8+
declare_oxc_lint!(
9+
/// ### What it does
10+
///
11+
/// Disallow certain types in boolean expressions.
12+
///
13+
/// ### Why is this bad?
14+
///
15+
/// Forbids usage of non-boolean types in expressions where a boolean is expected.
16+
/// `boolean` and `never` types are always allowed. Additional types which are
17+
/// considered safe in a boolean context can be configured via options.
18+
///
19+
/// The following nodes are checked:
20+
///
21+
/// - Arguments to the `!`, `&&`, and `||` operators
22+
/// - The condition in a conditional expression (`cond ? x : y`)
23+
/// - Conditions for `if`, `for`, `while`, and `do-while` statements.
24+
///
25+
/// ### Examples
26+
///
27+
/// Examples of **incorrect** code for this rule:
28+
/// ```ts
29+
/// const str = 'hello';
30+
/// if (str) {
31+
/// console.log('string');
32+
/// }
33+
///
34+
/// const num = 42;
35+
/// if (num) {
36+
/// console.log('number');
37+
/// }
38+
///
39+
/// const obj = { foo: 'bar' };
40+
/// if (obj) {
41+
/// console.log('object');
42+
/// }
43+
///
44+
/// declare const maybeString: string | undefined;
45+
/// if (maybeString) {
46+
/// console.log(maybeString);
47+
/// }
48+
///
49+
/// const result = str && num;
50+
/// const ternary = str ? 'yes' : 'no';
51+
/// ```
52+
///
53+
/// Examples of **correct** code for this rule:
54+
/// ```ts
55+
/// const str = 'hello';
56+
/// if (str !== '') {
57+
/// console.log('string');
58+
/// }
59+
///
60+
/// const num = 42;
61+
/// if (num !== 0) {
62+
/// console.log('number');
63+
/// }
64+
///
65+
/// const obj = { foo: 'bar' };
66+
/// if (obj !== null) {
67+
/// console.log('object');
68+
/// }
69+
///
70+
/// declare const maybeString: string | undefined;
71+
/// if (maybeString !== undefined) {
72+
/// console.log(maybeString);
73+
/// }
74+
///
75+
/// const bool = true;
76+
/// if (bool) {
77+
/// console.log('boolean');
78+
/// }
79+
/// ```
80+
StrictBooleanExpressions(tsgolint),
81+
typescript,
82+
pedantic,
83+
pending,
84+
);
85+
86+
impl Rule for StrictBooleanExpressions {}

0 commit comments

Comments
 (0)