8000 feat(eslint-plugin): [prefer-string-starts-ends-with] add allowSingle… · yeonjuan/typescript-eslint@32d8a8f · GitHub 10000
[go: up one dir, main page]

Skip to content

Commit 32d8a8f

Browse files
JoshuaKGoldbergauvredbradzacher
authored
feat(eslint-plugin): [prefer-string-starts-ends-with] add allowSingleElementEquality option (typescript-eslint#8374)
* feat(eslint-plugin): [prefer-string-starts-ends-with] add allowSingleElementEquality option * Update test snapshots * Update packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md Co-authored-by: auvred <61150013+auvred@users.noreply.github.com> --------- Co-authored-by: auvred <61150013+auvred@users.noreply.github.com> Co-authored-by: Brad Zacher <brad.zacher@gmail.com>
1 parent 4c8b06d commit 32d8a8f

File tree

6 files changed

+404
-259
lines changed

6 files changed

+404
-259
lines changed

.eslintrc.js

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@ module.exports = {
5353

5454
// TODO(#7130): Investigate changing these in or removing these from presets
5555
'@typescript-eslint/no-confusing-void-expression': 'off',
56-
'@typescript-eslint/prefer-string-starts-ends-with': 'off',
5756

5857
//
5958
// our plugin :D
@@ -91,6 +90,12 @@ module.exports = {
9190
allowBitwiseExpressions: true,
9291
},
9392
],
93+
'@typescript-eslint/prefer-string-starts-ends-with': [
94+
'error',
95+
{
96+
allowSingleElementEquality: 'always',
97+
},
98+
],
9499
'@typescript-eslint/unbound-method': 'off',
95100
'@typescript-eslint/restrict-template-expressions': [
96101
'error',

packages/eslint-plugin/docs/rules/prefer-string-starts-ends-with.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,26 @@ foo.startsWith('bar');
5252
foo.endsWith('bar');
5353
```
5454

55+
<!--/tabs-->
56+
57+
## Options
58+
59+
### `allowSingleElementEquality`
60+
61+
If switched to `'always'`, the rule will allow equality checks against the first or last character in a string.
62+
This can be preferable in projects that don't deal with special character encodings and prefer a more succinct style.
63+
64+
The following code is considered incorrect by default, but is allowed with `allowSingleElementEquality: 'always'`:
65+
66+
```ts option='{ "allowSingleElementEquality": "always" }' showPlaygroundButton
67+
declare const text: string;
68+
69+
text[0] === 'a';
70+
text[0] === text[0].toUpperCase();
71+
text[0] === text[1];
72+
text[text.length - 1] === 'b';
73+
```
74+
5575
## When Not To Use It
5676

5777
If you don't mind which style of string checking is used, you can turn this rule off safely.

packages/eslint-plugin/src/rules/prefer-string-starts-ends-with.ts

Lines changed: 35 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,19 @@ import {
1717
const EQ_OPERATORS = /^[=!]=/;
1818
const regexpp = new RegExpParser();
1919

20-
export default createRule({
20+
type AllowedSingleElementEquality = 'always' | 'never';
21+
22+
export type Options = [
23+
{
24+
allowSingleElementEquality?: AllowedSingleElementEquality;
25+
},
26+
];
27+
28+
type MessageIds = 'preferEndsWith' | 'preferStartsWith';
29+
30+
export default createRule<Options, MessageIds>({
2131
name: 'prefer-string-starts-ends-with',
22-
defaultOptions: [],
32+
defaultOptions: [{ allowSingleElementEquality: 'never' }],
2333

2434
meta: {
2535
type: 'suggestion',
@@ -33,11 +43,24 @@ export default createRule({
3343
preferStartsWith: "Use 'String#startsWith' method instead.",
3444
preferEndsWith: "Use the 'String#endsWith' method instead.",
3545
},
36-
schema: [],
46+
schema: [
47+
{
48+
additionalProperties: false,
49+
properties: {
50+
allowSingleElementEquality: {
51+
description:
52+
'Whether to allow equality checks against the first or last element of a string.',
53+
enum: ['always', 'never'],
54+
type: 'string',
55+
},
56+
},
57+
type: 'object',
58+
},
59+
],
3760
fixable: 'code',
3861
},
3962

40-
create(context) {
63+
create(context, [{ allowSingleElementEquality }]) {
4164
const globalScope = context.sourceCode.getScope(context.sourceCode.ast);
4265

4366
const services = getParserServices(context);
@@ -401,8 +424,15 @@ export default createRule({
401424
}
402425

403426
const isEndsWith = isLastIndexExpression(indexNode, node.object);
427+
if (allowSingleElementEquality === 'always' && isEndsWith) {
428+
return;
429+
}
430+
404431
const isStartsWith = !isEndsWith && isNumber(indexNode, 0);
405-
if (!isStartsWith && !isEndsWith) {
432+
if (
433+
(allowSingleElementEquality === 'always' && isStartsWith) ||
434+
(!isStartsWith && !isEndsWith)
435+
) {
406436
return;
407437
}
408438

packages/eslint-plugin/src/util/collectUnusedVariables.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -437,7 +437,7 @@ function isExported(variable: TSESLint.Scope.Variable): boolean {
437437
}
438438

439439
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
440-
return node.parent!.type.indexOf('Export') === 0;
440+
return node.parent!.type.startsWith('Export');
441441
});
442442
}
443443

0 commit comments

Comments
 (0)
0