@typescript-eslint/eslint-plugin
Version:
TypeScript plugin for ESLint
143 lines • 6.62 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const utils_1 = require("@typescript-eslint/utils");
const util_1 = require("../util");
const confusingOperators = new Set([
'=',
'==',
'===',
'in',
'instanceof',
]);
function isConfusingOperator(operator) {
return confusingOperators.has(operator);
}
exports.default = (0, util_1.createRule)({
name: 'no-confusing-non-null-assertion',
meta: {
type: 'problem',
docs: {
description: 'Disallow non-null assertion in locations that may be confusing',
recommended: 'stylistic',
},
hasSuggestions: true,
messages: {
confusingEqual: 'Confusing combination of non-null assertion and equality test like `a! == b`, which looks very similar to `a !== b`.',
confusingAssign: 'Confusing combination of non-null assertion and assignment like `a! = b`, which looks very similar to `a != b`.',
confusingOperator: 'Confusing combination of non-null assertion and `{{operator}}` operator like `a! {{operator}} b`, which might be misinterpreted as `!(a {{operator}} b)`.',
notNeedInEqualTest: 'Remove unnecessary non-null assertion (!) in equality test.',
notNeedInAssign: 'Remove unnecessary non-null assertion (!) in assignment left-hand side.',
notNeedInOperator: 'Remove possibly unnecessary non-null assertion (!) in the left operand of the `{{operator}}` operator.',
wrapUpLeft: 'Wrap the left-hand side in parentheses to avoid confusion with "{{operator}}" operator.',
},
schema: [],
},
defaultOptions: [],
create(context) {
function confusingOperatorToMessageData(operator) {
switch (operator) {
case '=':
return {
messageId: 'confusingAssign',
};
case '==':
case '===':
return {
messageId: 'confusingEqual',
};
case 'in':
case 'instanceof':
return {
messageId: 'confusingOperator',
data: { operator },
};
// istanbul ignore next
default:
operator;
throw new Error(`Unexpected operator ${operator}`);
}
}
return {
'BinaryExpression, AssignmentExpression'(node) {
const operator = node.operator;
if (isConfusingOperator(operator)) {
// Look for a non-null assertion as the last token on the left hand side.
// That way, we catch things like `1 + two! === 3`, even though the left
// hand side isn't a non-null assertion AST node.
const leftHandFinalToken = context.sourceCode.getLastToken(node.left);
const tokenAfterLeft = context.sourceCode.getTokenAfter(node.left);
if (leftHandFinalToken?.type === utils_1.AST_TOKEN_TYPES.Punctuator &&
leftHandFinalToken.value === '!' &&
tokenAfterLeft?.value !== ')') {
if (node.left.type === utils_1.AST_NODE_TYPES.TSNonNullExpression) {
let suggestions;
switch (operator) {
case '=':
suggestions = [
{
messageId: 'notNeedInAssign',
fix: (fixer) => fixer.remove(leftHandFinalToken),
},
];
break;
case '==':
case '===':
suggestions = [
{
messageId: 'notNeedInEqualTest',
fix: (fixer) => fixer.remove(leftHandFinalToken),
},
];
break;
case 'in':
case 'instanceof':
suggestions = [
{
messageId: 'notNeedInOperator',
data: { operator },
fix: (fixer) => fixer.remove(leftHandFinalToken),
},
{
messageId: 'wrapUpLeft',
data: { operator },
fix: wrapUpLeftFixer(node),
},
];
break;
// istanbul ignore next
default:
operator;
return;
}
context.report({
node,
...confusingOperatorToMessageData(operator),
suggest: suggestions,
});
}
else {
context.report({
node,
...confusingOperatorToMessageData(operator),
suggest: [
{
messageId: 'wrapUpLeft',
data: { operator },
fix: wrapUpLeftFixer(node),
},
],
});
}
}
}
},
};
},
});
function wrapUpLeftFixer(node) {
return (fixer) => [
fixer.insertTextBefore(node.left, '('),
fixer.insertTextAfter(node.left, ')'),
];
}
//# sourceMappingURL=no-confusing-non-null-assertion.js.map
;