Skip to content

Commit d042f18

Browse files
authored
fix(useOptionalChain): support optional chaining using typeof (#7415)
1 parent b906112 commit d042f18

14 files changed

+3480
-18
lines changed

.changeset/lazy-animals-burn.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@biomejs/biome": patch
3+
---
4+
5+
Fixed [#7212](https://github.com/biomejs/biome/issues/7212), now the [`useOptionalChain`](https://biomejs.dev/linter/rules/use-optional-chain/) rule recognizes optional chaining using `typeof` (e.g., `typeof foo !== 'undefined' && foo.bar`).
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// chained members
2+
typeof foo !== 'undefined' && foo.bar;
3+
typeof foo.bar !== 'undefined' && foo.bar.baz;
4+
typeof foo !== 'undefined' && foo();
5+
typeof foo.bar !== 'undefined' && foo.bar();
6+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && foo.bar.baz.buzz;
7+
typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && foo.bar.baz.buzz;
8+
9+
// case with a jump (i.e. a non-'undefined'ish prop)
10+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && foo.bar.baz.buzz;
11+
typeof foo.bar !== 'undefined' && foo.bar.baz.buzz;
12+
13+
// case where for some reason there is a doubled up expression
14+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && typeof foo.bar.baz !== 'undefined' && foo.bar.baz.buzz;
15+
typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && typeof foo.bar.baz !== 'undefined' && foo.bar.baz.buzz;
16+
17+
// chained members with element access
18+
typeof foo !== 'undefined' && typeof foo[bar] !== 'undefined' && typeof foo[bar].baz !== 'undefined' && foo[bar].baz.buzz;
19+
20+
// case with a jump (i.e. a non-'undefined'ish prop)
21+
typeof foo !== 'undefined' && typeof foo[bar].baz !== 'undefined' && foo[bar].baz.buzz;
22+
23+
// chained calls
24+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && foo.bar.baz.buzz();
25+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && typeof foo.bar.baz.buzz !== 'undefined' && foo.bar.baz.buzz();
26+
typeof foo.bar !== 'undefined' && typeof foo.bar.baz !== 'undefined' && typeof foo.bar.baz.buzz !== 'undefined' && foo.bar.baz.buzz();
27+
28+
// case with a jump (i.e. a non-'undefined'ish prop)
29+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && foo.bar.baz.buzz();
30+
typeof foo.bar !== 'undefined' && foo.bar.baz.buzz();
31+
32+
// case with a jump (i.e. a non-'undefined'ish prop)
33+
typeof foo !== 'undefined' && typeof foo.bar !== 'undefined' && typeof foo.bar.baz.buzz !== 'undefined' && foo.bar.baz.buzz();
34+
35+
// case with a call expr inside the chain for some inefficient reason
36+
typeof foo !== 'undefined' && typeof foo.bar() !== 'undefined' && typeof foo.bar().baz !== 'undefined' && typeof foo.bar().baz.buzz !== 'undefined' && foo.bar().baz.buzz();
37+
38+
39+
// chained members (double quotes)
40+
typeof foo !== "undefined" && foo.bar;
41+
typeof foo.bar !== "undefined" && foo.bar.baz;
42+
typeof foo !== "undefined" && foo();
43+
typeof foo.bar !== "undefined" && foo.bar();
44+
typeof foo !== "undefined" && typeof foo.bar !== "undefined" && typeof foo.bar.baz !== "undefined" && foo.bar.baz.buzz;
45+
typeof foo.bar !== "undefined" && typeof foo.bar.baz !== "undefined" && foo.bar.baz.buzz;
46+
47+
// chained members (backticks)
48+
typeof foo !== `undefined` && foo.bar;
49+
typeof foo.bar !== `undefined` && foo.bar.baz;
50+
typeof foo !== `undefined` && foo();
51+
typeof foo.bar !== `undefined` && foo.bar();
52+
typeof foo !== `undefined` && typeof foo.bar !== `undefined` && typeof foo.bar.baz !== `undefined` && foo.bar.baz.buzz;
53+
typeof foo.bar !== `undefined` && typeof foo.bar.baz !== `undefined` && foo.bar.baz.buzz;

crates/biome_js_analyze/tests/specs/complexity/useOptionalChain/typeofLogicalAndCases1.ts.snap

Lines changed: 792 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// chained members
2+
typeof foo != 'undefined' && foo.bar;
3+
typeof foo.bar != 'undefined' && foo.bar.baz;
4+
typeof foo != 'undefined' && foo();
5+
typeof foo.bar != 'undefined' && foo.bar();
6+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && foo.bar.baz.buzz;
7+
typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && foo.bar.baz.buzz;
8+
9+
// case with a jump (i.e. a non-'undefined'ish prop)
10+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && foo.bar.baz.buzz;
11+
typeof foo.bar != 'undefined' && foo.bar.baz.buzz;
12+
13+
// case where for some reason there is a doubled up expression
14+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && typeof foo.bar.baz != 'undefined' && foo.bar.baz.buzz;
15+
typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && typeof foo.bar.baz != 'undefined' && foo.bar.baz.buzz;
16+
17+
// chained members with element access
18+
typeof foo != 'undefined' && typeof foo[bar] != 'undefined' && typeof foo[bar].baz != 'undefined' && foo[bar].baz.buzz;
19+
20+
// case with a jump (i.e. a non-'undefined'ish prop)
21+
typeof foo != 'undefined' && typeof foo[bar].baz != 'undefined' && foo[bar].baz.buzz;
22+
23+
// chained calls
24+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && foo.bar.baz.buzz();
25+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && typeof foo.bar.baz.buzz != 'undefined' && foo.bar.baz.buzz();
26+
typeof foo.bar != 'undefined' && typeof foo.bar.baz != 'undefined' && typeof foo.bar.baz.buzz != 'undefined' && foo.bar.baz.buzz();
27+
28+
// case with a jump (i.e. a non-'undefined'ish prop)
29+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && foo.bar.baz.buzz();
30+
typeof foo.bar != 'undefined' && foo.bar.baz.buzz();
31+
32+
// case with a jump (i.e. a non-'undefined'ish prop)
33+
typeof foo != 'undefined' && typeof foo.bar != 'undefined' && typeof foo.bar.baz.buzz != 'undefined' && foo.bar.baz.buzz();
34+
35+
// case with a call expr inside the chain for some inefficient reason
36+
typeof foo != 'undefined' && typeof foo.bar() != 'undefined' && typeof foo.bar().baz != 'undefined' && typeof foo.bar().baz.buzz != 'undefined' && foo.bar().baz.buzz();
37+
38+
39+
// chained members (double quotes)
40+
typeof foo != "undefined" && foo.bar;
41+
typeof foo.bar != "undefined" && foo.bar.baz;
42+
typeof foo != "undefined" && foo();
43+
typeof foo.bar != "undefined" && foo.bar();
44+
typeof foo != "undefined" && typeof foo.bar != "undefined" && typeof foo.bar.baz != "undefined" && foo.bar.baz.buzz;
45+
typeof foo.bar != "undefined" && typeof foo.bar.baz != "undefined" && foo.bar.baz.buzz;
46+
47+
// chained members (backticks)
48+
typeof foo != `undefined` && foo.bar;
49+
typeof foo.bar != `undefined` && foo.bar.baz;
50+
typeof foo != `undefined` && foo();
51+
typeof foo.bar != `undefined` && foo.bar();
52+
typeof foo != `undefined` && typeof foo.bar != `undefined` && typeof foo.bar.baz != `undefined` && foo.bar.baz.buzz;
53+
typeof foo.bar != `undefined` && typeof foo.bar.baz != `undefined` && foo.bar.baz.buzz;

0 commit comments

Comments
 (0)