|
17 | 17 | * under the License. |
18 | 18 | */ |
19 | 19 | import { ESLintUtils } from '@typescript-eslint/utils'; |
| 20 | +import ts from "typescript"; |
20 | 21 |
|
21 | 22 | const createRule = ESLintUtils.RuleCreator(name => `https://example.com/rule/${name}`) |
22 | 23 |
|
23 | | -const TYPE_SUGGESTIONS = { |
24 | | - 'Record': 'Use Dictionary instead', |
25 | | - 'Partial': 'Use spec-defined aliases instead', |
26 | | - 'Required': 'Use spec-defined aliases instead', |
27 | | - 'Pick': 'Use spec-defined aliases instead', |
28 | | - 'Omit': 'Use spec-defined aliases instead', |
29 | | - 'Map': 'Use Dictionary instead', |
30 | | - 'Set': 'Use an array type instead (e.g., string[])', |
31 | | - 'WeakMap': 'Use Dictionary instead', |
32 | | - 'WeakSet': 'Use an array type instead', |
33 | | -}; |
34 | | - |
35 | 24 | export default createRule({ |
36 | 25 | name: 'no-same-name-as-enclosing-type', |
37 | 26 | create(context) { |
38 | 27 | return { |
39 | | - TSTypeReference(node) { |
40 | | - console.log("BBBBBBBBBBB") |
41 | | - const typeName = node.typeName.name; |
42 | | - if (TYPE_SUGGESTIONS[typeName]) { |
43 | | - context.report({ |
44 | | - node, |
45 | | - messageId: 'noNativeType', |
46 | | - data: { |
47 | | - type: typeName, |
48 | | - suggestion: TYPE_SUGGESTIONS[typeName] |
| 28 | + ClassDeclaration(node) { |
| 29 | + if (!node.id || !node.id.name) { |
| 30 | + return; // anonymous class - nothing to check |
| 31 | + } |
| 32 | + const services = ESLintUtils.getParserServices(context) |
| 33 | + const tsClass = services.esTreeNodeToTSNodeMap.get(node); |
| 34 | + if (!tsClass || !tsClass.members) { |
| 35 | + return; // no fields |
| 36 | + } |
| 37 | + const className = node.id.name; |
| 38 | + for (const member of tsClass.members) { |
| 39 | + // Property declarations on the class (instance or static fields) |
| 40 | + if (ts.isPropertyDeclaration(member) || ts.isPropertySignature?.(member)) { |
| 41 | + const name = member.name; |
| 42 | + if (name && name.kind === ts.SyntaxKind.Identifier) { |
| 43 | + if (String(name.escapedText).toUpperCase() === className.toUpperCase()) { |
| 44 | + context.report({ |
| 45 | + node, |
| 46 | + messageId: 'shouldNotUseClassNameForFieldNames', |
| 47 | + data: { |
| 48 | + class: className, |
| 49 | + suggestion: 'Fields in a class should not have the same name as the class itself.' |
| 50 | + } |
| 51 | + }) |
| 52 | + } |
| 53 | + } |
49 | 54 | } |
50 | | - }) |
51 | 55 | } |
52 | 56 | }, |
53 | 57 | } |
54 | 58 | }, |
55 | 59 | meta: { |
56 | 60 | docs: { |
57 | | - description: 'TypeScript native utility and collection types not allowed, use spec-defined aliases', |
| 61 | + description: 'Classes having fields with the same name as the class is breaking for some client libraries.', |
58 | 62 | }, |
59 | 63 | messages: { |
60 | | - noNativeType: 'Native TypeScript type "{{type}}" is not allowed. {{suggestion}}.' |
| 64 | + shouldNotUseClassNameForFieldNames: 'Class "{{class}}" has invalid fields. {{suggestion}}.' |
61 | 65 | }, |
62 | 66 | type: 'suggestion', |
63 | 67 | }, |
|
0 commit comments