@typescript-eslint/eslint-plugin
Version:
TypeScript plugin for ESLint
150 lines (149 loc) • 6.9 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
const scope_manager_1 = require("@typescript-eslint/scope-manager");
const utils_1 = require("@typescript-eslint/utils");
const util_1 = require("../util");
const UNNECESSARY_OPERATORS = new Set(['??=', '&&=', '=', '||=']);
exports.default = (0, util_1.createRule)({
name: 'no-unnecessary-parameter-property-assignment',
meta: {
type: 'suggestion',
docs: {
description: 'Disallow unnecessary assignment of constructor property parameter',
},
messages: {
unnecessaryAssign: 'This assignment is unnecessary since it is already assigned by a parameter property.',
},
schema: [],
},
defaultOptions: [],
create(context) {
const reportInfoStack = [];
function isThisMemberExpression(node) {
return (node.type === utils_1.AST_NODE_TYPES.MemberExpression &&
node.object.type === utils_1.AST_NODE_TYPES.ThisExpression);
}
function getPropertyName(node) {
if (!isThisMemberExpression(node)) {
return null;
}
if (node.property.type === utils_1.AST_NODE_TYPES.Identifier) {
return node.property.name;
}
if (node.computed) {
return (0, util_1.getStaticStringValue)(node.property);
}
return null;
}
function findParentFunction(node) {
if (!node ||
node.type === utils_1.AST_NODE_TYPES.FunctionDeclaration ||
node.type === utils_1.AST_NODE_TYPES.FunctionExpression ||
node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression) {
return node;
}
return findParentFunction(node.parent);
}
function findParentPropertyDefinition(node) {
if (!node || node.type === utils_1.AST_NODE_TYPES.PropertyDefinition) {
return node;
}
return findParentPropertyDefinition(node.parent);
}
function isConstructorFunctionExpression(node) {
return (node?.type === utils_1.AST_NODE_TYPES.FunctionExpression &&
utils_1.ASTUtils.isConstructor(node.parent));
}
function isReferenceFromParameter(node) {
const scope = context.sourceCode.getScope(node);
const rightRef = scope.references.find(ref => ref.identifier.name === node.name);
return rightRef?.resolved?.defs.at(0)?.type === scope_manager_1.DefinitionType.Parameter;
}
function isParameterPropertyWithName(node, name) {
return (node.type === utils_1.AST_NODE_TYPES.TSParameterProperty &&
((node.parameter.type === utils_1.AST_NODE_TYPES.Identifier && // constructor (public foo) {}
node.parameter.name === name) ||
(node.parameter.type === utils_1.AST_NODE_TYPES.AssignmentPattern && // constructor (public foo = 1) {}
node.parameter.left.type === utils_1.AST_NODE_TYPES.Identifier &&
node.parameter.left.name === name)));
}
function getIdentifier(node) {
if (node.type === utils_1.AST_NODE_TYPES.Identifier) {
return node;
}
if (node.type === utils_1.AST_NODE_TYPES.TSAsExpression ||
node.type === utils_1.AST_NODE_TYPES.TSNonNullExpression) {
return getIdentifier(node.expression);
}
return null;
}
function isArrowIIFE(node) {
return (node.type === utils_1.AST_NODE_TYPES.ArrowFunctionExpression &&
node.parent.type === utils_1.AST_NODE_TYPES.CallExpression);
}
return {
ClassBody() {
reportInfoStack.push({
assignedBeforeConstructor: new Set(),
assignedBeforeUnnecessary: new Set(),
unnecessaryAssignments: [],
});
},
'ClassBody:exit'() {
const { assignedBeforeConstructor, unnecessaryAssignments } = (0, util_1.nullThrows)(reportInfoStack.pop(), 'The top stack should exist');
unnecessaryAssignments.forEach(({ name, node }) => {
if (assignedBeforeConstructor.has(name)) {
return;
}
context.report({
node,
messageId: 'unnecessaryAssign',
});
});
},
"MethodDefinition[kind='constructor'] > FunctionExpression AssignmentExpression"(node) {
const leftName = getPropertyName(node.left);
if (!leftName) {
return;
}
let functionNode = findParentFunction(node);
if (functionNode && isArrowIIFE(functionNode)) {
functionNode = findParentFunction(functionNode.parent);
}
if (!isConstructorFunctionExpression(functionNode)) {
return;
}
const { assignedBeforeUnnecessary, unnecessaryAssignments } = (0, util_1.nullThrows)(reportInfoStack.at(reportInfoStack.length - 1), 'The top of stack should exist');
if (!UNNECESSARY_OPERATORS.has(node.operator)) {
assignedBeforeUnnecessary.add(leftName);
return;
}
const rightId = getIdentifier(node.right);
if (leftName !== rightId?.name || !isReferenceFromParameter(rightId)) {
return;
}
const hasParameterProperty = functionNode.params.some(param => isParameterPropertyWithName(param, rightId.name));
if (hasParameterProperty && !assignedBeforeUnnecessary.has(leftName)) {
unnecessaryAssignments.push({
name: leftName,
node,
});
}
},
'PropertyDefinition AssignmentExpression'(node) {
const name = getPropertyName(node.left);
if (!name) {
return;
}
const functionNode = findParentFunction(node);
if (functionNode &&
!(isArrowIIFE(functionNode) &&
findParentPropertyDefinition(node)?.value === functionNode.parent)) {
return;
}
const { assignedBeforeConstructor } = (0, util_1.nullThrows)(reportInfoStack.at(-1), 'The top stack should exist');
assignedBeforeConstructor.add(name);
},
};
},
});
;