2 * @fileoverview Rule to flag statements that use != and == instead of !== and ===
3 * @author Nicholas C. Zakas
8 //------------------------------------------------------------------------------
10 //------------------------------------------------------------------------------
12 const astUtils = require("./utils/ast-utils");
14 //------------------------------------------------------------------------------
16 //------------------------------------------------------------------------------
23 description: "require the use of `===` and `!==`",
24 category: "Best Practices",
26 url: "https://eslint.org/docs/rules/eqeqeq"
41 enum: ["always", "never", "ignore"]
44 additionalProperties: false
47 additionalItems: false
53 enum: ["smart", "allow-null"]
56 additionalItems: false
64 unexpected: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'."
69 const config = context.options[0] || "always";
70 const options = context.options[1] || {};
71 const sourceCode = context.getSourceCode();
73 const nullOption = (config === "always")
74 ? options.null || "always"
76 const enforceRuleForNull = (nullOption === "always");
77 const enforceInverseRuleForNull = (nullOption === "never");
80 * Checks if an expression is a typeof expression
81 * @param {ASTNode} node The node to check
82 * @returns {boolean} if the node is a typeof expression
84 function isTypeOf(node) {
85 return node.type === "UnaryExpression" && node.operator === "typeof";
89 * Checks if either operand of a binary expression is a typeof operation
90 * @param {ASTNode} node The node to check
91 * @returns {boolean} if one of the operands is typeof
94 function isTypeOfBinary(node) {
95 return isTypeOf(node.left) || isTypeOf(node.right);
99 * Checks if operands are literals of the same type (via typeof)
100 * @param {ASTNode} node The node to check
101 * @returns {boolean} if operands are of same type
104 function areLiteralsAndSameType(node) {
105 return node.left.type === "Literal" && node.right.type === "Literal" &&
106 typeof node.left.value === typeof node.right.value;
110 * Checks if one of the operands is a literal null
111 * @param {ASTNode} node The node to check
112 * @returns {boolean} if operands are null
115 function isNullCheck(node) {
116 return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);
120 * Reports a message for this rule.
121 * @param {ASTNode} node The binary expression node that was checked
122 * @param {string} expectedOperator The operator that was expected (either '==', '!=', '===', or '!==')
126 function report(node, expectedOperator) {
127 const operatorToken = sourceCode.getFirstTokenBetween(
130 token => token.value === node.operator
135 loc: operatorToken.loc,
136 messageId: "unexpected",
137 data: { expectedOperator, actualOperator: node.operator },
140 // If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix.
141 if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) {
142 return fixer.replaceText(operatorToken, expectedOperator);
150 BinaryExpression(node) {
151 const isNull = isNullCheck(node);
153 if (node.operator !== "==" && node.operator !== "!=") {
154 if (enforceInverseRuleForNull && isNull) {
155 report(node, node.operator.slice(0, -1));
160 if (config === "smart" && (isTypeOfBinary(node) ||
161 areLiteralsAndSameType(node) || isNull)) {
165 if (!enforceRuleForNull && isNull) {
169 report(node, `${node.operator}=`);