diff --git a/packages/eslint-plugin/src/rules/member-naming.ts b/packages/eslint-plugin/src/rules/member-naming.ts index 9995b6cf1e3..d850ef69060 100644 --- a/packages/eslint-plugin/src/rules/member-naming.ts +++ b/packages/eslint-plugin/src/rules/member-naming.ts @@ -1,4 +1,7 @@ -import { TSESTree } from '@typescript-eslint/experimental-utils'; +import { + TSESTree, + AST_NODE_TYPES, +} from '@typescript-eslint/experimental-utils'; import * as util from '../util'; interface Config { @@ -61,37 +64,73 @@ export default util.createRule({ return acc; }, {}); - /** - * Check that the property name matches the convention for its - * accessibility. - * @param {ASTNode} node the named node to evaluate. - * @returns {void} - * @private - */ + function getParameterNode( + node: TSESTree.TSParameterProperty, + ): TSESTree.Identifier | null { + if (node.parameter.type === AST_NODE_TYPES.AssignmentPattern) { + return node.parameter.left as TSESTree.Identifier; + } + + if (node.parameter.type === AST_NODE_TYPES.Identifier) { + return node.parameter; + } + + return null; + } + + function validateParameterName(node: TSESTree.TSParameterProperty): void { + const parameterNode = getParameterNode(node); + if (!parameterNode) { + return; + } + + validate(parameterNode, parameterNode.name, node.accessibility); + } + function validateName( node: TSESTree.MethodDefinition | TSESTree.ClassProperty, ): void { - const name = util.getNameFromClassMember(node, sourceCode); - const accessibility: Modifiers = node.accessibility || 'public'; - const convention = conventions[accessibility]; - - const method = node as TSESTree.MethodDefinition; - if (method.kind === 'constructor') { + if ( + node.type === AST_NODE_TYPES.MethodDefinition && + node.kind === 'constructor' + ) { return; } + validate( + node.key, + util.getNameFromClassMember(node, sourceCode), + node.accessibility, + ); + } + + /** + * Check that the name matches the convention for its accessibility. + * @param {ASTNode} node the named node to evaluate. + * @param {string} name + * @param {Modifiers} accessibility + * @returns {void} + * @private + */ + function validate( + node: TSESTree.Identifier | TSESTree.Expression, + name: string, + accessibility: Modifiers = 'public', + ): void { + const convention = conventions[accessibility]; if (!convention || convention.test(name)) { return; } context.report({ - node: node.key, + node, messageId: 'incorrectName', data: { accessibility, name, convention }, }); } return { + TSParameterProperty: validateParameterName, MethodDefinition: validateName, ClassProperty: validateName, }; diff --git a/packages/eslint-plugin/tests/rules/member-naming.test.ts b/packages/eslint-plugin/tests/rules/member-naming.test.ts index 96ec4b104f3..851d70ea706 100644 --- a/packages/eslint-plugin/tests/rules/member-naming.test.ts +++ b/packages/eslint-plugin/tests/rules/member-naming.test.ts @@ -86,6 +86,30 @@ class Class { }, ], }, + + { + code: ` +class Test { + constructor(public __a: string, protected __b: string, private __c: string = 100) {} +} + `, + options: [ + { + protected: '^__', + private: '^__', + public: '^__', + }, + ], + }, + { + code: + // Semantically invalid test case, TS has to throw an error. + ` +class Foo { + constructor(private ...name: string[], private [test]: [string]) {} +} + `, + }, ], invalid: [ { @@ -329,5 +353,51 @@ class Class { }, ], }, + { + code: ` +class Test { + constructor(public a: string, protected b: string, private c: string = 100) {} +} + `, + options: [ + { + public: '^__', + protected: '^__', + private: '^__', + }, + ], + errors: [ + { + messageId: 'incorrectName', + data: { + accessibility: 'public', + convention: '/^__/', + name: 'a', + }, + line: 3, + column: 24, + }, + { + messageId: 'incorrectName', + data: { + accessibility: 'protected', + convention: '/^__/', + name: 'b', + }, + line: 3, + column: 45, + }, + { + messageId: 'incorrectName', + data: { + accessibility: 'private', + convention: '/^__/', + name: 'c', + }, + line: 3, + column: 64, + }, + ], + }, ], });