2 * @fileoverview Rule to disallow unsafe optional chaining
8 const UNSAFE_ARITHMETIC_OPERATORS
= new Set(["+", "-", "/", "*", "%", "**"]);
9 const UNSAFE_ASSIGNMENT_OPERATORS
= new Set(["+=", "-=", "/=", "*=", "%=", "**="]);
10 const UNSAFE_RELATIONAL_OPERATORS
= new Set(["in", "instanceof"]);
13 * Checks whether a node is a destructuring pattern or not
14 * @param {ASTNode} node node to check
15 * @returns {boolean} `true` if a node is a destructuring pattern, otherwise `false`
17 function isDestructuringPattern(node
) {
18 return node
.type
=== "ObjectPattern" || node
.type
=== "ArrayPattern";
21 /** @type {import('../shared/types').Rule} */
27 description
: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
29 url
: "https://eslint.org/docs/rules/no-unsafe-optional-chaining"
34 disallowArithmeticOperators
: {
39 additionalProperties
: false
43 unsafeOptionalChain
: "Unsafe usage of optional chaining. If it short-circuits with 'undefined' the evaluation will throw TypeError.",
44 unsafeArithmetic
: "Unsafe arithmetic operation on optional chaining. It can result in NaN."
49 const options
= context
.options
[0] || {};
50 const disallowArithmeticOperators
= (options
.disallowArithmeticOperators
) || false;
53 * Reports unsafe usage of optional chaining
54 * @param {ASTNode} node node to report
57 function reportUnsafeUsage(node
) {
59 messageId
: "unsafeOptionalChain",
65 * Reports unsafe arithmetic operation on optional chaining
66 * @param {ASTNode} node node to report
69 function reportUnsafeArithmetic(node
) {
71 messageId
: "unsafeArithmetic",
77 * Checks and reports if a node can short-circuit with `undefined` by optional chaining.
78 * @param {ASTNode} [node] node to check
79 * @param {Function} reportFunc report function
82 function checkUndefinedShortCircuit(node
, reportFunc
) {
87 case "LogicalExpression":
88 if (node
.operator
=== "||" || node
.operator
=== "??") {
89 checkUndefinedShortCircuit(node
.right
, reportFunc
);
90 } else if (node
.operator
=== "&&") {
91 checkUndefinedShortCircuit(node
.left
, reportFunc
);
92 checkUndefinedShortCircuit(node
.right
, reportFunc
);
95 case "SequenceExpression":
96 checkUndefinedShortCircuit(
97 node
.expressions
[node
.expressions
.length
- 1],
101 case "ConditionalExpression":
102 checkUndefinedShortCircuit(node
.consequent
, reportFunc
);
103 checkUndefinedShortCircuit(node
.alternate
, reportFunc
);
105 case "AwaitExpression":
106 checkUndefinedShortCircuit(node
.argument
, reportFunc
);
108 case "ChainExpression":
117 * Checks unsafe usage of optional chaining
118 * @param {ASTNode} node node to check
121 function checkUnsafeUsage(node
) {
122 checkUndefinedShortCircuit(node
, reportUnsafeUsage
);
126 * Checks unsafe arithmetic operations on optional chaining
127 * @param {ASTNode} node node to check
130 function checkUnsafeArithmetic(node
) {
131 checkUndefinedShortCircuit(node
, reportUnsafeArithmetic
);
135 "AssignmentExpression, AssignmentPattern"(node
) {
136 if (isDestructuringPattern(node
.left
)) {
137 checkUnsafeUsage(node
.right
);
140 "ClassDeclaration, ClassExpression"(node
) {
141 checkUnsafeUsage(node
.superClass
);
143 CallExpression(node
) {
144 if (!node
.optional
) {
145 checkUnsafeUsage(node
.callee
);
148 NewExpression(node
) {
149 checkUnsafeUsage(node
.callee
);
151 VariableDeclarator(node
) {
152 if (isDestructuringPattern(node
.id
)) {
153 checkUnsafeUsage(node
.init
);
156 MemberExpression(node
) {
157 if (!node
.optional
) {
158 checkUnsafeUsage(node
.object
);
161 TaggedTemplateExpression(node
) {
162 checkUnsafeUsage(node
.tag
);
164 ForOfStatement(node
) {
165 checkUnsafeUsage(node
.right
);
167 SpreadElement(node
) {
168 if (node
.parent
&& node
.parent
.type
!== "ObjectExpression") {
169 checkUnsafeUsage(node
.argument
);
172 BinaryExpression(node
) {
173 if (UNSAFE_RELATIONAL_OPERATORS
.has(node
.operator
)) {
174 checkUnsafeUsage(node
.right
);
177 disallowArithmeticOperators
&&
178 UNSAFE_ARITHMETIC_OPERATORS
.has(node
.operator
)
180 checkUnsafeArithmetic(node
.right
);
181 checkUnsafeArithmetic(node
.left
);
184 WithStatement(node
) {
185 checkUnsafeUsage(node
.object
);
187 UnaryExpression(node
) {
189 disallowArithmeticOperators
&&
190 UNSAFE_ARITHMETIC_OPERATORS
.has(node
.operator
)
192 checkUnsafeArithmetic(node
.argument
);
195 AssignmentExpression(node
) {
197 disallowArithmeticOperators
&&
198 UNSAFE_ASSIGNMENT_OPERATORS
.has(node
.operator
)
200 checkUnsafeArithmetic(node
.right
);