UNPKG

5.78 kBJavaScriptView Raw
1/**
2 * @fileoverview Rule to flag statements that use != and == instead of !== and ===
3 * @author Nicholas C. Zakas
4 */
5
6"use strict";
7
8//------------------------------------------------------------------------------
9// Requirements
10//------------------------------------------------------------------------------
11
12const astUtils = require("./utils/ast-utils");
13
14//------------------------------------------------------------------------------
15// Rule Definition
16//------------------------------------------------------------------------------
17
18module.exports = {
19 meta: {
20 type: "suggestion",
21
22 docs: {
23 description: "require the use of `===` and `!==`",
24 category: "Best Practices",
25 recommended: false,
26 url: "https://eslint.org/docs/rules/eqeqeq"
27 },
28
29 schema: {
30 anyOf: [
31 {
32 type: "array",
33 items: [
34 {
35 enum: ["always"]
36 },
37 {
38 type: "object",
39 properties: {
40 null: {
41 enum: ["always", "never", "ignore"]
42 }
43 },
44 additionalProperties: false
45 }
46 ],
47 additionalItems: false
48 },
49 {
50 type: "array",
51 items: [
52 {
53 enum: ["smart", "allow-null"]
54 }
55 ],
56 additionalItems: false
57 }
58 ]
59 },
60
61 fixable: "code",
62
63 messages: {
64 unexpected: "Expected '{{expectedOperator}}' and instead saw '{{actualOperator}}'."
65 }
66 },
67
68 create(context) {
69 const config = context.options[0] || "always";
70 const options = context.options[1] || {};
71 const sourceCode = context.getSourceCode();
72
73 const nullOption = (config === "always")
74 ? options.null || "always"
75 : "ignore";
76 const enforceRuleForNull = (nullOption === "always");
77 const enforceInverseRuleForNull = (nullOption === "never");
78
79 /**
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
83 */
84 function isTypeOf(node) {
85 return node.type === "UnaryExpression" && node.operator === "typeof";
86 }
87
88 /**
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
92 * @private
93 */
94 function isTypeOfBinary(node) {
95 return isTypeOf(node.left) || isTypeOf(node.right);
96 }
97
98 /**
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
102 * @private
103 */
104 function areLiteralsAndSameType(node) {
105 return node.left.type === "Literal" && node.right.type === "Literal" &&
106 typeof node.left.value === typeof node.right.value;
107 }
108
109 /**
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
113 * @private
114 */
115 function isNullCheck(node) {
116 return astUtils.isNullLiteral(node.right) || astUtils.isNullLiteral(node.left);
117 }
118
119 /**
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 '!==')
123 * @returns {void}
124 * @private
125 */
126 function report(node, expectedOperator) {
127 const operatorToken = sourceCode.getFirstTokenBetween(
128 node.left,
129 node.right,
130 token => token.value === node.operator
131 );
132
133 context.report({
134 node,
135 loc: operatorToken.loc,
136 messageId: "unexpected",
137 data: { expectedOperator, actualOperator: node.operator },
138 fix(fixer) {
139
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);
143 }
144 return null;
145 }
146 });
147 }
148
149 return {
150 BinaryExpression(node) {
151 const isNull = isNullCheck(node);
152
153 if (node.operator !== "==" && node.operator !== "!=") {
154 if (enforceInverseRuleForNull && isNull) {
155 report(node, node.operator.slice(0, -1));
156 }
157 return;
158 }
159
160 if (config === "smart" && (isTypeOfBinary(node) ||
161 areLiteralsAndSameType(node) || isNull)) {
162 return;
163 }
164
165 if (!enforceRuleForNull && isNull) {
166 return;
167 }
168
169 report(node, `${node.operator}=`);
170 }
171 };
172
173 }
174};