Skip to content

Commit c6f72fb

Browse files
authored
fix(eslint-plugin): [prefer-ts-expect-error] support block comments (typescript-eslint#2541)
1 parent 53a3cbc commit c6f72fb

File tree

4 files changed

+149
-29
lines changed

4 files changed

+149
-29
lines changed

packages/eslint-plugin/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ Pro Tip: For larger codebases you may want to consider splitting our linting int
161161
| [`@typescript-eslint/prefer-reduce-type-parameter`](./docs/rules/prefer-reduce-type-parameter.md) | Prefer using type parameter when calling `Array#reduce` instead of casting | | :wrench: | :thought_balloon: |
162162
| [`@typescript-eslint/prefer-regexp-exec`](./docs/rules/prefer-regexp-exec.md) | Enforce that `RegExp#exec` is used instead of `String#match` if no global flag is provided | :heavy_check_mark: | | :thought_balloon: |
163163
| [`@typescript-eslint/prefer-string-starts-ends-with`](./docs/rules/prefer-string-starts-ends-with.md) | Enforce the use of `String#startsWith` and `String#endsWith` instead of other equivalent methods of checking substrings | | :wrench: | :thought_balloon: |
164-
| [`@typescript-eslint/prefer-ts-expect-error`](./docs/rules/prefer-ts-expect-error.md) | Recommends using `// @ts-expect-error` over `// @ts-ignore` | | :wrench: | |
164+
| [`@typescript-eslint/prefer-ts-expect-error`](./docs/rules/prefer-ts-expect-error.md) | Recommends using `@ts-expect-error` over `@ts-ignore` | | :wrench: | |
165165
| [`@typescript-eslint/promise-function-async`](./docs/rules/promise-function-async.md) | Requires any function or method that returns a Promise to be marked async | | | :thought_balloon: |
166166
| [`@typescript-eslint/require-array-sort-compare`](./docs/rules/require-array-sort-compare.md) | Requires `Array#sort` calls to always provide a `compareFunction` | | | :thought_balloon: |
167167
| [`@typescript-eslint/restrict-plus-operands`](./docs/rules/restrict-plus-operands.md) | When adding two variables, operands must both be of type number or of type string | :heavy_check_mark: | | :thought_balloon: |

packages/eslint-plugin/docs/rules/prefer-ts-expect-error.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# Recommends using `// @ts-expect-error` over `// @ts-ignore` (`prefer-ts-expect-error`)
1+
# Recommends using `@ts-expect-error` over `@ts-ignore` (`prefer-ts-expect-error`)
22

3-
TypeScript allows you to suppress all errors on a line by placing a single-line comment starting with `@ts-ignore` immediately before the erroring line.
3+
TypeScript allows you to suppress all errors on a line by placing a single-line comment or a comment block line starting with `@ts-ignore` immediately before the erroring line.
44
While powerful, there is no way to know if a `@ts-ignore` is actually suppressing an error without manually investigating what happens when the `@ts-ignore` is removed.
55

66
This means its easy for `@ts-ignore`s to be forgotten about, and remain in code even after the error they were suppressing is fixed.
@@ -20,6 +20,15 @@ Examples of **incorrect** code for this rule:
2020
// @ts-ignore
2121
const str: string = 1;
2222

23+
/**
24+
* Explaining comment
25+
*
26+
* @ts-ignore */
27+
const multiLine: number = 'value';
28+
29+
/** @ts-ignore */
30+
const block: string = 1;
31+
2332
const isOptionEnabled = (key: string): boolean => {
2433
// @ts-ignore: if key isn't in globalOptions it'll be undefined which is false
2534
return !!globalOptions[key];
@@ -32,6 +41,15 @@ Examples of **correct** code for this rule:
3241
// @ts-expect-error
3342
const str: string = 1;
3443

44+
/**
45+
* Explaining comment
46+
*
47+
* @ts-expect-error */
48+
const multiLine: number = 'value';
49+
50+
/** @ts-expect-error */
51+
const block: string = 1;
52+
3553
const isOptionEnabled = (key: string): boolean => {
3654
// @ts-expect-error: if key isn't in globalOptions it'll be undefined which is false
3755
return !!globalOptions[key];
@@ -40,7 +58,7 @@ const isOptionEnabled = (key: string): boolean => {
4058

4159
## When Not To Use It
4260

43-
If you are not using TypeScript 3.9 (or greater), then you will not be able to use this rule, as the directive is not supported
61+
If you are **NOT** using TypeScript 3.9 (or greater), then you will not be able to use this rule, as the directive is not supported
4462

4563
## Further Reading
4664

packages/eslint-plugin/src/rules/prefer-ts-expect-error.ts

Lines changed: 51 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import { AST_TOKEN_TYPES } from '@typescript-eslint/experimental-utils';
21
import * as util from '../util';
2+
import {
3+
AST_TOKEN_TYPES,
4+
TSESTree,
5+
} from '@typescript-eslint/experimental-utils';
6+
import {
7+
RuleFixer,
8+
RuleFix,
9+
} from '@typescript-eslint/experimental-utils/dist/ts-eslint';
310

411
type MessageIds = 'preferExpectErrorComment';
512

@@ -8,44 +15,70 @@ export default util.createRule<[], MessageIds>({
815
meta: {
916
type: 'problem',
1017
docs: {
11-
description:
12-
'Recommends using `// @ts-expect-error` over `// @ts-ignore`',
18+
description: 'Recommends using `@ts-expect-error` over `@ts-ignore`',
1319
category: 'Best Practices',
1420
recommended: false,
1521
},
1622
fixable: 'code',
1723
messages: {
1824
preferExpectErrorComment:
19-
'Use "// @ts-expect-error" to ensure an error is actually being suppressed.',
25+
'Use "@ts-expect-error" to ensure an error is actually being suppressed.',
2026
},
2127
schema: [],
2228
},
2329
defaultOptions: [],
2430
create(context) {
25-
const tsIgnoreRegExp = /^\/*\s*@ts-ignore/;
31+
const tsIgnoreRegExpSingleLine = /^\s*\/?\s*@ts-ignore/;
32+
const tsIgnoreRegExpMultiLine = /^\s*(?:\/|\*)*\s*@ts-ignore/;
2633
const sourceCode = context.getSourceCode();
2734

35+
function isLineComment(comment: TSESTree.Comment): boolean {
36+
return comment.type === AST_TOKEN_TYPES.Line;
37+
}
38+
39+
function getLastCommentLine(comment: TSESTree.Comment): string {
40+
if (isLineComment(comment)) {
41+
return comment.value;
42+
}
43+
44+
// For multiline comments - we look at only the last line.
45+
const commentlines = comment.value.split('\n');
46+
return commentlines[commentlines.length - 1];
47+
}
48+
49+
function isValidTsIgnorePresent(comment: TSESTree.Comment): boolean {
50+
const line = getLastCommentLine(comment);
51+
return isLineComment(comment)
52+
? tsIgnoreRegExpSingleLine.test(line)
53+
: tsIgnoreRegExpMultiLine.test(line);
54+
}
55+
2856
return {
2957
Program(): void {
3058
const comments = sourceCode.getAllComments();
31-
3259
comments.forEach(comment => {
33-
if (comment.type !== AST_TOKEN_TYPES.Line) {
34-
return;
35-
}
60+
if (isValidTsIgnorePresent(comment)) {
61+
const lineCommentRuleFixer = (fixer: RuleFixer): RuleFix =>
62+
fixer.replaceText(
63+
comment,
64+
`//${comment.value.replace('@ts-ignore', '@ts-expect-error')}`,
65+
);
66+
67+
const blockCommentRuleFixer = (fixer: RuleFixer): RuleFix =>
68+
fixer.replaceText(
69+
comment,
70+
`/*${comment.value.replace(
71+
'@ts-ignore',
72+
'@ts-expect-error',
73+
)}*/`,
74+
);
3675

37-
if (tsIgnoreRegExp.test(comment.value)) {
3876
context.report({
3977
node: comment,
4078
messageId: 'preferExpectErrorComment',
41-
fix: fixer =>
42-
fixer.replaceText(
43-
comment,
44-
`//${comment.value.replace(
45-
'@ts-ignore',
46-
'@ts-expect-error',
47-
)}`,
48-
),
79+
fix: isLineComment(comment)
80+
? lineCommentRuleFixer
81+
: blockCommentRuleFixer,
4982
});
5083
}
5184
});

packages/eslint-plugin/tests/rules/prefer-ts-expect-error.test.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,12 @@ ruleTester.run('prefer-ts-expect-error', rule, {
1010
'// @ts-nocheck',
1111
'// @ts-check',
1212
'// just a comment containing @ts-ignore somewhere',
13-
'/* @ts-ignore */',
14-
'/** @ts-ignore */',
1513
`
16-
/*
17-
// @ts-ignore in a block
18-
*/
14+
{
15+
/*
16+
just a comment containing @ts-ignore somewhere in a block
17+
*/
18+
}
1919
`,
2020
'// @ts-expect-error',
2121
`
@@ -24,6 +24,15 @@ if (false) {
2424
console.log('hello');
2525
}
2626
`,
27+
`
28+
/**
29+
* Explaining comment
30+
*
31+
* @ts-expect-error
32+
*
33+
* Not last line
34+
* */
35+
`,
2736
],
2837
invalid: [
2938
{
@@ -50,8 +59,8 @@ if (false) {
5059
],
5160
},
5261
{
53-
code: '/////@ts-ignore: Suppress next line',
54-
output: '/////@ts-expect-error: Suppress next line',
62+
code: '///@ts-ignore: Suppress next line',
63+
output: '///@ts-expect-error: Suppress next line',
5564
errors: [
5665
{
5766
messageId: 'preferExpectErrorComment',
@@ -81,5 +90,65 @@ if (false) {
8190
},
8291
],
8392
},
93+
{
94+
code: '/* @ts-ignore */',
95+
output: '/* @ts-expect-error */',
96+
errors: [
97+
{
98+
messageId: 'preferExpectErrorComment',
99+
line: 1,
100+
column: 1,
101+
},
102+
],
103+
},
104+
{
105+
code: `
106+
/**
107+
* Explaining comment
108+
*
109+
* @ts-ignore */
110+
`,
111+
output: `
112+
/**
113+
* Explaining comment
114+
*
115+
* @ts-expect-error */
116+
`,
117+
errors: [
118+
{
119+
messageId: 'preferExpectErrorComment',
120+
line: 2,
121+
column: 1,
122+
},
123+
],
124+
},
125+
{
126+
code: '/* @ts-ignore in a single block */',
127+
output: '/* @ts-expect-error in a single block */',
128+
errors: [
129+
{
130+
messageId: 'preferExpectErrorComment',
131+
line: 1,
132+
column: 1,
133+
},
134+
],
135+
},
136+
{
137+
code: `
138+
/*
139+
// @ts-ignore in a block with single line comments */
140+
`,
141+
output: `
142+
/*
143+
// @ts-expect-error in a block with single line comments */
144+
`,
145+
errors: [
146+
{
147+
messageId: 'preferExpectErrorComment',
148+
line: 2,
149+
column: 1,
150+
},
151+
],
152+
},
84153
],
85154
});

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