Skip to content

Commit 8a0fd18

Browse files
feat(eslint-plugin): [ban-ts-comments] add "allow-with-description" option (typescript-eslint#2099)
1 parent 1ae1d01 commit 8a0fd18

File tree

4 files changed

+255
-24
lines changed

4 files changed

+255
-24
lines changed

packages/eslint-plugin/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
9292
| [`@typescript-eslint/adjacent-overload-signatures`](./docs/rules/adjacent-overload-signatures.md) | Require that member overloads be consecutive | :heavy_check_mark: | | |
9393
| [`@typescript-eslint/array-type`](./docs/rules/array-type.md) | Requires using either `T[]` or `Array<T>` for arrays | | :wrench: | |
9494
| [`@typescript-eslint/await-thenable`](./docs/rules/await-thenable.md) | Disallows awaiting a value that is not a Thenable | :heavy_check_mark: | | :thought_balloon: |
95-
| [`@typescript-eslint/ban-ts-comment`](./docs/rules/ban-ts-comment.md) | Bans `// @ts-<directive>` comments from being used | :heavy_check_mark: | | |
95+
| [`@typescript-eslint/ban-ts-comment`](./docs/rules/ban-ts-comment.md) | Bans `// @ts-<directive>` comments from being used or requires descriptions after directive | :heavy_check_mark: | | |
9696
| [`@typescript-eslint/ban-types`](./docs/rules/ban-types.md) | Bans specific types from being used | :heavy_check_mark: | :wrench: | |
9797
| [`@typescript-eslint/class-literal-property-style`](./docs/rules/class-literal-property-style.md) | Ensures that literals on classes are exposed in a consistent style | | :wrench: | |
9898
| [`@typescript-eslint/consistent-type-assertions`](./docs/rules/consistent-type-assertions.md) | Enforces consistent usage of type assertions | | | |

packages/eslint-plugin/docs/rules/ban-ts-comment.md

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Bans `// @ts-<directive>` comments from being used (`ban-ts-comment`)
1+
# Bans `// @ts-<directive>` comments from being used or requires descriptions after directive (`ban-ts-comment`)
22

33
TypeScript provides several directive comments that can be used to alter how it processes files.
44
Using these to suppress TypeScript Compiler Errors reduces the effectiveness of TypeScript overall.
@@ -21,20 +21,24 @@ The configuration looks like this:
2121

2222
```ts
2323
interface Options {
24-
'ts-expect-error'?: boolean;
25-
'ts-ignore'?: boolean;
26-
'ts-nocheck'?: boolean;
27-
'ts-check'?: boolean;
24+
'ts-expect-error'?: boolean | 'allow-with-description';
25+
'ts-ignore'?: boolean | 'allow-with-description';
26+
'ts-nocheck'?: boolean | 'allow-with-description';
27+
'ts-check'?: boolean | 'allow-with-description';
28+
minimumDescriptionLength?: number;
2829
}
2930

3031
const defaultOptions: Options = {
3132
'ts-expect-error': true,
3233
'ts-ignore': true,
3334
'ts-nocheck': true,
3435
'ts-check': false,
36+
minimumDescriptionLength: 3,
3537
};
3638
```
3739

40+
### `ts-expect-error`, `ts-ignore`, `ts-nocheck`, `ts-check` directives
41+
3842
A value of `true` for a particular directive means that this rule will report if it finds any usage of said directive.
3943

4044
For example, with the defaults above the following patterns are considered warnings:
@@ -55,6 +59,50 @@ if (false) {
5559
}
5660
```
5761

62+
### `allow-with-description`
63+
64+
A value of `'allow-with-description'` for a particular directive means that this rule will report if it finds a directive that does not have a description following the directive (on the same line).
65+
66+
For example, with `{ 'ts-expect-error': 'allow-with-description' }` the following pattern is considered a warning:
67+
68+
```ts
69+
if (false) {
70+
// @ts-expect-error
71+
console.log('hello');
72+
}
73+
```
74+
75+
The following pattern is not a warning:
76+
77+
```ts
78+
if (false) {
79+
// @ts-expect-error: Unreachable code error
80+
console.log('hello');
81+
}
82+
```
83+
84+
### `minimumDescriptionLength`
85+
86+
Use `minimumDescriptionLength` to set a minimum length for descriptions when using the `allow-with-description` option for a directive.
87+
88+
For example, with `{ 'ts-expect-error': 'allow-with-description', minimumDescriptionLength: 10 }` the following pattern is considered a warning:
89+
90+
```ts
91+
if (false) {
92+
// @ts-expect-error: TODO
93+
console.log('hello');
94+
}
95+
```
96+
97+
The following pattern is not a warning:
98+
99+
```ts
100+
if (false) {
101+
// @ts-expect-error The rationale for this override is described in issue #1337 on GitLab
102+
console.log('hello');
103+
}
104+
```
105+
58106
## When Not To Use It
59107

60108
If you want to use all of the TypeScript directives.

packages/eslint-plugin/src/rules/ban-ts-comment.ts

Lines changed: 73 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,96 @@ import { AST_TOKEN_TYPES } from '@typescript-eslint/experimental-utils';
22
import * as util from '../util';
33

44
interface Options {
5-
'ts-expect-error'?: boolean;
6-
'ts-ignore'?: boolean;
7-
'ts-nocheck'?: boolean;
8-
'ts-check'?: boolean;
5+
'ts-expect-error'?: boolean | 'allow-with-description';
6+
'ts-ignore'?: boolean | 'allow-with-description';
7+
'ts-nocheck'?: boolean | 'allow-with-description';
8+
'ts-check'?: boolean | 'allow-with-description';
9+
minimumDescriptionLength?: number;
910
}
1011

12+
export const defaultMinimumDescriptionLength = 3;
13+
1114
const defaultOptions: [Options] = [
1215
{
1316
'ts-expect-error': true,
1417
'ts-ignore': true,
1518
'ts-nocheck': true,
1619
'ts-check': false,
20+
minimumDescriptionLength: defaultMinimumDescriptionLength,
1721
},
1822
];
1923

20-
type MessageIds = 'tsDirectiveComment';
24+
type MessageIds =
25+
| 'tsDirectiveComment'
26+
| 'tsDirectiveCommentRequiresDescription';
2127

2228
export default util.createRule<[Options], MessageIds>({
2329
name: 'ban-ts-comment',
2430
meta: {
2531
type: 'problem',
2632
docs: {
27-
description: 'Bans `// @ts-<directive>` comments from being used',
33+
description:
34+
'Bans `// @ts-<directive>` comments from being used or requires descriptions after directive',
2835
category: 'Best Practices',
2936
recommended: 'error',
3037
},
3138
messages: {
3239
tsDirectiveComment:
3340
'Do not use "// @ts-{{directive}}" because it alters compilation errors.',
41+
tsDirectiveCommentRequiresDescription:
42+
'Include a description after the "// @ts-{{directive}}" directive to explain why the @ts-{{directive}} is necessary. The description must be {{minimumDescriptionLength}} characters or longer.',
3443
},
3544
schema: [
3645
{
3746
type: 'object',
3847
properties: {
3948
'ts-expect-error': {
40-
type: 'boolean',
41-
default: true,
49+
oneOf: [
50+
{
51+
type: 'boolean',
52+
default: true,
53+
},
54+
{
55+
enum: ['allow-with-description'],
56+
},
57+
],
4258
},
4359
'ts-ignore': {
44-
type: 'boolean',
45-
default: true,
60+
oneOf: [
61+
{
62+
type: 'boolean',
63+
default: true,
64+
},
65+
{
66+
enum: ['allow-with-description'],
67+
},
68+
],
4669
},
4770
'ts-nocheck': {
48-
type: 'boolean',
49-
default: true,
71+
oneOf: [
72+
{
73+
type: 'boolean',
74+
default: true,
75+
},
76+
{
77+
enum: ['allow-with-description'],
78+
},
79+
],
5080
},
5181
'ts-check': {
52-
type: 'boolean',
53-
default: false,
82+
oneOf: [
83+
{
84+
type: 'boolean',
85+
default: true,
86+
},
87+
{
88+
enum: ['allow-with-description'],
89+
},
90+
],
91+
},
92+
minimumDescriptionLength: {
93+
type: 'number',
94+
default: defaultMinimumDescriptionLength,
5495
},
5596
},
5697
additionalProperties: false,
@@ -59,7 +100,7 @@ export default util.createRule<[Options], MessageIds>({
59100
},
60101
defaultOptions,
61102
create(context, [options]) {
62-
const tsCommentRegExp = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)/;
103+
const tsCommentRegExp = /^\/*\s*@ts-(expect-error|ignore|check|nocheck)(.*)/;
63104
const sourceCode = context.getSourceCode();
64105

65106
return {
@@ -71,17 +112,32 @@ export default util.createRule<[Options], MessageIds>({
71112
return;
72113
}
73114

74-
const [, directive] = tsCommentRegExp.exec(comment.value) ?? [];
115+
const [, directive, description] =
116+
tsCommentRegExp.exec(comment.value) ?? [];
75117

76118
const fullDirective = `ts-${directive}` as keyof Options;
77119

78-
if (options[fullDirective]) {
120+
const option = options[fullDirective];
121+
if (option === true) {
79122
context.report({
80123
data: { directive },
81124
node: comment,
82125
messageId: 'tsDirectiveComment',
83126
});
84127
}
128+
129+
if (option === 'allow-with-description') {
130+
const {
131+
minimumDescriptionLength = defaultMinimumDescriptionLength,
132+
} = options;
133+
if (description.trim().length < minimumDescriptionLength) {
134+
context.report({
135+
data: { directive, minimumDescriptionLength },
136+
node: comment,
137+
messageId: 'tsDirectiveCommentRequiresDescription',
138+
});
139+
}
140+
}
85141
});
86142
},
87143
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy