Spaces:
Runtime error
Runtime error
/** | |
* @fileoverview Rule to flag statements that use != and == instead of !== and === | |
* @author Nicholas C. Zakas | |
*/ | |
; | |
//------------------------------------------------------------------------------ | |
// Requirements | |
//------------------------------------------------------------------------------ | |
const astUtils = require("./utils/ast-utils"); | |
//------------------------------------------------------------------------------ | |
// Rule Definition | |
//------------------------------------------------------------------------------ | |
module.exports = { | |
meta: { | |
type: "suggestion", | |
docs: { | |
description: "require the use of `===` and `!==`", | |
category: "Best Practices", | |
recommended: false, | |
url: "https://eslint.org/docs/rules/eqeqeq" | |
}, | |
schema: { | |
anyOf: [ | |
{ | |
type: "array", | |
items: [ | |
{ | |
enum: ["always"] | |
}, | |
{ | |
type: "object", | |
properties: { | |
null: { | |
enum: ["always", "never", "ignore"] | |
} | |
}, | |
additionalProperties: false | |
} | |
], | |
additionalItems: false | |
}, | |
{ | |
type: "array", | |
items: [ | |
{ | |
enum: ["smart", "allow-null"] | |
} | |
], | |
additionalItems: false | |
} | |
] | |
}, | |
fixable: "code", | |
messages: { | |
unexpected: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'." | |
} | |
}, | |
create(context) { | |
const config = context.options[0] || "always"; | |
const options = context.options[1] || {}; | |
const sourceCode = context.getSourceCode(); | |
const nullOption = (config === "always") | |
? options.null || "always" | |
: "ignore"; | |
const enforceRuleForNull = (nullOption === "always"); | |
const enforceInverseRuleForNull = (nullOption === "never"); | |
/** | |
* Checks if an expression is a typeof expression | |
* @param {ASTNode} node The node to check | |
* @returns {boolean} if the node is a typeof expression | |
*/ | |
function isTypeOf(node) { | |
return node.type === "UnaryExpression" && node.operator === "typeof"; | |
} | |
/** | |
* Checks if either operand of a binary expression is a typeof operation | |
* @param {ASTNode} node The node to check | |
* @returns {boolean} if one of the operands is typeof | |
* @private | |
*/ | |
function isTypeOfBinary(node) { | |
return isTypeOf(node.left) || isTypeOf(node.right); | |
} | |
/** | |
* Checks if operands are literals of the same type (via typeof) | |
* @param {ASTNode} node The node to check | |
* @returns {boolean} if operands are of same type | |
* @private | |
*/ | |
function areLiteralsAndSameType(node) { | |
return node.left.type === "Literal" && node.right.type === "Literal" && | |
typeof node.left.value === typeof node.right.value; | |
} | |
/** | |
* Checks if one of the operands is a literal null | |
* @param {ASTNode} node The node to check | |
* @returns {boolean} if operands are null | |
* @private | |
*/ | |
function isNullCheck(node) { | |
return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left); | |
} | |
/** | |
* Reports a message for this rule. | |
* @param {ASTNode} node The binary expression node that was checked | |
* @param {string} expectedOperator The operator that was expected (either '==', '!=', '===', or '!==') | |
* @returns {void} | |
* @private | |
*/ | |
function report(node, expectedOperator) { | |
const operatorToken = sourceCode.getFirstTokenBetween( | |
node.left, | |
node.right, | |
token => token.value === node.operator | |
); | |
context.report({ | |
node, | |
loc: operatorToken.loc, | |
messageId: "unexpected", | |
data: { expectedOperator, actualOperator: node.operator }, | |
fix(fixer) { | |
// If the comparison is a `typeof` comparison or both sides are literals with the same type, then it's safe to fix. | |
if (isTypeOfBinary(node) || areLiteralsAndSameType(node)) { | |
return fixer.replaceText(operatorToken, expectedOperator); | |
} | |
return null; | |
} | |
}); | |
} | |
return { | |
BinaryExpression(node) { | |
const isNull = isNullCheck(node); | |
if (node.operator !== "==" && node.operator !== "!=") { | |
if (enforceInverseRuleForNull && isNull) { | |
report(node, node.operator.slice(0, -1)); | |
} | |
return; | |
} | |
if (config === "smart" && (isTypeOfBinary(node) || | |
areLiteralsAndSameType(node) || isNull)) { | |
return; | |
} | |
if (!enforceRuleForNull && isNull) { | |
return; | |
} | |
report(node, `${node.operator}=`); | |
} | |
}; | |
} | |
}; | |