Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(eslint-plugin): [naming-convention] allow selecting only const variables #2291

Merged
17 changes: 16 additions & 1 deletion packages/eslint-plugin/docs/rules/naming-convention.md
Expand Up @@ -192,7 +192,7 @@ There are two types of selectors, individual selectors, and grouped selectors.
Individual Selectors match specific, well-defined sets. There is no overlap between each of the individual selectors.

- `variable` - matches any `var` / `let` / `const` variable name.
- Allowed `modifiers`: none.
- Allowed `modifiers`: `const`.
- Allowed `types`: `boolean`, `string`, `number`, `function`, `array`.
- `function` - matches any named function declaration or named function expression.
- Allowed `modifiers`: none.
Expand Down Expand Up @@ -309,6 +309,21 @@ Group Selectors are provided for convenience, and essentially bundle up sets of
}
```

### Enforce that all const variables are in UPPER_CASE

```json
{
"@typescript-eslint/naming-convention": [
"error",
{
"selector": "variable",
"modifiers": ["const"],
"format": ["UPPER_CASE"]
}
]
}
```

### Enforce that type parameters (generics) are prefixed with `T`

This allows you to emulate the old `generic-type-naming` rule.
Expand Down
27 changes: 19 additions & 8 deletions packages/eslint-plugin/src/rules/naming-convention.ts
Expand Up @@ -80,12 +80,13 @@ type MetaSelectorsString = keyof typeof MetaSelectors;
type IndividualAndMetaSelectorsString = SelectorsString | MetaSelectorsString;

enum Modifiers {
readonly = 1 << 0,
static = 1 << 1,
public = 1 << 2,
protected = 1 << 3,
private = 1 << 4,
abstract = 1 << 5,
const = 1 << 0,
readonly = 1 << 1,
static = 1 << 2,
public = 1 << 3,
protected = 1 << 4,
private = 1 << 5,
abstract = 1 << 6,
}
type ModifiersString = keyof typeof Modifiers;

Expand Down Expand Up @@ -255,7 +256,7 @@ const SCHEMA: JSONSchema.JSONSchema4 = {
...selectorSchema('default', false, util.getEnumNames(Modifiers)),

...selectorSchema('variableLike', false),
...selectorSchema('variable', true),
...selectorSchema('variable', true, ['const']),
...selectorSchema('function', false),
...selectorSchema('parameter', true),

Expand Down Expand Up @@ -439,8 +440,18 @@ export default util.createRule<Options, MessageIds>({
const identifiers: TSESTree.Identifier[] = [];
getIdentifiersFromPattern(node.id, identifiers);

const modifiers = new Set<Modifiers>();
const parent = node.parent;
if (
parent &&
parent.type === AST_NODE_TYPES.VariableDeclaration &&
parent.kind === 'const'
) {
modifiers.add(Modifiers.const);
}

identifiers.forEach(i => {
validator(i);
validator(i, modifiers);
});
},

Expand Down
22 changes: 21 additions & 1 deletion packages/eslint-plugin/tests/rules/naming-convention.test.ts
Expand Up @@ -626,6 +626,10 @@ ruleTester.run('naming-convention', rule, {
},
{
code: `
declare const ANY_UPPER_CASE: any;
declare const ANY_UPPER_CASE: any | null;
declare const ANY_UPPER_CASE: any | null | undefined;

declare const string_camelCase: string;
declare const string_camelCase: string | null;
declare const string_camelCase: string | null | undefined;
Expand All @@ -647,6 +651,12 @@ ruleTester.run('naming-convention', rule, {
`,
parserOptions,
options: [
{
selector: 'variable',
modifiers: ['const'],
format: ['UPPER_CASE'],
prefix: ['ANY_'],
},
{
selector: 'variable',
types: ['string'],
Expand Down Expand Up @@ -824,6 +834,10 @@ ruleTester.run('naming-convention', rule, {
},
{
code: `
declare const any_camelCase01: any;
declare const any_camelCase02: any | null;
declare const any_camelCase03: any | null | undefined;

declare const string_camelCase01: string;
declare const string_camelCase02: string | null;
declare const string_camelCase03: string | null | undefined;
Expand All @@ -844,6 +858,12 @@ ruleTester.run('naming-convention', rule, {
declare const boolean_camelCase16: true | false | null | undefined;
`,
options: [
{
selector: 'variable',
modifiers: ['const'],
format: ['UPPER_CASE'],
prefix: ['any_'],
},
{
selector: 'variable',
types: ['string'],
Expand All @@ -864,7 +884,7 @@ ruleTester.run('naming-convention', rule, {
},
],
parserOptions,
errors: Array(16).fill({ messageId: 'doesNotMatchFormatTrimmed' }),
errors: Array(19).fill({ messageId: 'doesNotMatchFormatTrimmed' }),
},
{
code: `
Expand Down