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";
26 description: "disallow use of optional chaining in contexts where the `undefined` value is not allowed",
27 category: "Possible Errors",
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);