]>
Commit | Line | Data |
---|---|---|
456be15e TL |
1 | # disallow use of optional chaining in contexts where the `undefined` value is not allowed (no-unsafe-optional-chaining) |
2 | ||
3 | The optional chaining (`?.`) expression can short-circuit with a return value of `undefined`. Therefore, treating an evaluated optional chaining expression as a function, object, number, etc., can cause TypeError or unexpected results. For example: | |
4 | ||
5 | ```js | |
6 | var obj = undefined; | |
7 | ||
8 | 1 in obj?.foo; // TypeError | |
9 | with (obj?.foo); // TypeError | |
10 | for (bar of obj?.foo); // TypeError | |
11 | bar instanceof obj?.foo; // TypeError | |
12 | const { bar } = obj?.foo; // TypeError | |
13 | ``` | |
14 | ||
15 | Also, parentheses limit the scope of short-circuiting in chains. For example: | |
16 | ||
17 | ```js | |
18 | var obj = undefined; | |
19 | ||
20 | (obj?.foo)(); // TypeError | |
21 | (obj?.foo).bar; // TypeError | |
22 | ``` | |
23 | ||
24 | ## Rule Details | |
25 | ||
26 | This rule aims to detect some cases where the use of optional chaining doesn't prevent runtime errors. In particular, it flags optional chaining expressions in positions where short-circuiting to `undefined` causes throwing a TypeError afterward. | |
27 | ||
28 | Examples of **incorrect** code for this rule: | |
29 | ||
30 | ```js | |
31 | /*eslint no-unsafe-optional-chaining: "error"*/ | |
32 | ||
33 | (obj?.foo)(); | |
34 | ||
35 | (obj?.foo).bar; | |
36 | ||
37 | (foo?.()).bar; | |
38 | ||
39 | (foo?.()).bar(); | |
40 | ||
41 | (obj?.foo ?? obj?.bar)(); | |
42 | ||
43 | (foo || obj?.foo)(); | |
44 | ||
45 | (obj?.foo && foo)(); | |
46 | ||
47 | (foo ? obj?.foo : bar)(); | |
48 | ||
49 | (foo, obj?.bar).baz; | |
50 | ||
51 | (obj?.foo)`template`; | |
52 | ||
53 | new (obj?.foo)(); | |
54 | ||
55 | [...obj?.foo]; | |
56 | ||
57 | bar(...obj?.foo); | |
58 | ||
59 | 1 in obj?.foo; | |
60 | ||
61 | bar instanceof obj?.foo; | |
62 | ||
63 | for (bar of obj?.foo); | |
64 | ||
65 | const { bar } = obj?.foo; | |
66 | ||
67 | [{ bar } = obj?.foo] = []; | |
68 | ||
69 | with (obj?.foo); | |
70 | ||
71 | class A extends obj?.foo {} | |
72 | ||
73 | var a = class A extends obj?.foo {}; | |
74 | ||
75 | async function foo () { | |
76 | const { bar } = await obj?.foo; | |
77 | (await obj?.foo)(); | |
78 | (await obj?.foo).bar; | |
79 | } | |
80 | ``` | |
81 | ||
82 | Examples of **correct** code for this rule: | |
83 | ||
84 | ```js | |
85 | /*eslint no-unsafe-optional-chaining: "error"*/ | |
86 | ||
87 | (obj?.foo)?.(); | |
88 | ||
89 | obj?.foo(); | |
90 | ||
91 | (obj?.foo ?? bar)(); | |
92 | ||
93 | obj?.foo.bar; | |
94 | ||
5422a9cc TL |
95 | obj.foo?.bar; |
96 | ||
456be15e TL |
97 | foo?.()?.bar; |
98 | ||
99 | (obj?.foo ?? bar)`template`; | |
100 | ||
101 | new (obj?.foo ?? bar)(); | |
102 | ||
103 | var baz = {...obj?.foo}; | |
104 | ||
105 | const { bar } = obj?.foo || baz; | |
106 | ||
107 | async function foo () { | |
108 | const { bar } = await obj?.foo || baz; | |
109 | (await obj?.foo)?.(); | |
110 | (await obj?.foo)?.bar; | |
111 | } | |
112 | ``` | |
113 | ||
114 | ## Options | |
115 | ||
116 | This rule has an object option: | |
117 | ||
118 | - `disallowArithmeticOperators`: Disallow arithmetic operations on optional chaining expressions (Default `false`). If this is `true`, this rule warns arithmetic operations on optional chaining expressions, which possibly result in `NaN`. | |
119 | ||
120 | ### disallowArithmeticOperators | |
121 | ||
122 | With this option set to `true` the rule is enforced for: | |
123 | ||
124 | - Unary operators: `-`, `+` | |
125 | - Arithmetic operators: `+`, `-`, `/`, `*`, `%`, `**` | |
126 | - Assignment operators: `+=`, `-=`, `/=`, `*=`, `%=`, `**=` | |
127 | ||
128 | Examples of additional **incorrect** code for this rule with the `{ "disallowArithmeticOperators": true }` option: | |
129 | ||
130 | ```js | |
131 | /*eslint no-unsafe-optional-chaining: ["error", { "disallowArithmeticOperators": true }]*/ | |
132 | ||
133 | +obj?.foo; | |
134 | -obj?.foo; | |
135 | ||
136 | obj?.foo + bar; | |
137 | obj?.foo - bar; | |
138 | obj?.foo / bar; | |
139 | obj?.foo * bar; | |
140 | obj?.foo % bar; | |
141 | obj?.foo ** bar; | |
142 | ||
143 | baz += obj?.foo; | |
144 | baz -= obj?.foo; | |
145 | baz /= obj?.foo; | |
146 | baz *= obj?.foo; | |
147 | baz %= obj?.foo; | |
148 | baz **= obj?.foo; | |
149 | ||
150 | async function foo () { | |
151 | +await obj?.foo; | |
152 | await obj?.foo + bar; | |
153 | baz += await obj?.foo; | |
154 | } | |
155 | ``` |