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

Repo: enable more core ESLint rules internally #9523

Open
abrahamguo opened this issue Jul 9, 2024 · 24 comments
Open

Repo: enable more core ESLint rules internally #9523

abrahamguo opened this issue Jul 9, 2024 · 24 comments
Labels
accepting prs Go ahead, send a pull request that resolves this issue repo maintenance things to do with maintenance of the repo, and not with code/docs

Comments

@abrahamguo
Copy link
Contributor

Suggestion

I was looking through the core ESLint rules that we do not have turned on internally, and I found these rules that might be beneficial to enable. Thoughts?

array-callback-return (2 occurrences)

docs

Array.prototype.filter() expects a value to be returned at the end of arrow function.
eslint-plugin/tests/configs.test.ts:60
typescript-eslint/tests/configs.test.ts:64

arrow-body-style (41 occurrences)

docs

Unexpected block statement surrounding arrow body; move the returned value immediately after the =>.
eslint-plugin/src/rules/class-literal-property-style.ts:173
eslint-plugin/src/rules/max-params.ts:81
eslint-plugin/src/rules/naming-convention.ts:714
eslint-plugin/src/rules/no-array-delete.ts:92
eslint-plugin/src/rules/no-duplicate-type-constituents.ts:189
eslint-plugin/src/rules/no-extraneous-class.ts:72
eslint-plugin/src/rules/no-meaningless-void-operator.ts:52
eslint-plugin/src/rules/no-type-alias.ts:229
eslint-plugin/src/rules/no-unnecessary-template-expression.ts:41
eslint-plugin/src/rules/no-useless-template-literals.ts:42
eslint-plugin/src/rules/non-nullable-type-assertion-style.ts:94
eslint-plugin/src/rules/prefer-enum-initializers.ts:42
eslint-plugin/src/rules/prefer-enum-initializers.ts:49
eslint-plugin/src/rules/prefer-enum-initializers.ts:56
eslint-plugin/src/rules/prefer-find.ts:258
eslint-plugin/src/rules/prefer-find.ts:298
eslint-plugin/src/rules/prefer-function-type.ts:145
eslint-plugin/tests/rules/block-spacing.test.ts:78
type-utils/src/builtinSymbolLikes.ts:81
website/plugins/blog-footer.ts:6
website/plugins/generated-rule-docs/index.ts:17
website/src/components/ErrorsViewer.tsx:136
website/src/components/config/ConfigEditor.tsx:99
website/src/components/config/ConfigTypeScript.tsx:33
website/src/components/editor/LoadedEditor.tsx:270
website/src/components/hooks/useResizeObserver.ts:7
website/src/components/inputs/Text.tsx:17
website/src/components/linter/createParser.ts:27
website/src/pages/play.tsx:16

Unexpected block statement surrounding arrow body; parenthesize the returned value and move it immediately after the =>.
eslint-plugin/src/rules/ban-tslint-comment.ts:33
eslint-plugin/tests/rules/prefer-readonly-parameter-types.test.ts:530
rule-tester/src/RuleTester.ts:327
rule-tester/tests/RuleTester.test.ts:95
rule-tester/tests/RuleTester.test.ts:138
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:23
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:35
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:51
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:56
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:66
website/sidebars/sidebar.rules.js:3
website/sidebars/sidebar.rules.js:13

class-methods-use-this (13 occurrences)

docs

Expected this to be used by class method BreakStatement.
scope-manager/src/referencer/Referencer.ts:386

Expected this to be used by class method ContinueStatement.
scope-manager/src/referencer/Referencer.ts:425

Expected this to be used by class method Decorator.
scope-manager/src/referencer/PatternVisitor.ts:90

Expected this to be used by class method ExportAllDeclaration.
scope-manager/src/referencer/Referencer.ts:429

Expected this to be used by class method ImportAttribute.
scope-manager/src/referencer/Referencer.ts:813

Expected this to be used by class method isES6.
scope-manager/src/ScopeManager.ts:87

Expected this to be used by class method isStrictModeSupported.
scope-manager/src/ScopeManager.ts:83

Expected this to be used by class method isValidResolution.
scope-manager/src/scope/ScopeBase.ts:374

Expected this to be used by class method JSXClosingElement.
scope-manager/src/referencer/Referencer.ts:517

Expected this to be used by class method MetaProperty.
scope-manager/src/referencer/Referencer.ts:575

Expected this to be used by class method PrivateIdentifier.
scope-manager/src/referencer/ClassVisitor.ts:319
scope-manager/src/referencer/Referencer.ts:584

Expected this to be used by class method TSTypeAnnotation.
scope-manager/src/referencer/PatternVisitor.ts:136

default-param-last (1 occurrence)

docs

Default parameters should be last.
scope-manager/src/scope/ScopeBase.ts:444

no-await-in-loop (2 occurrences)

docs

Unexpected await inside a loop.
repo-tools/src/generate-contributors.mts:59
repo-tools/src/generate-lib.mts:250

no-lonely-if (9 occurrences)

docs

Unexpected if as the only statement in an else block.
eslint-plugin/src/rules/consistent-type-imports.ts:181
eslint-plugin/src/rules/consistent-type-imports.ts:735
eslint-plugin/src/rules/func-call-spacing.ts:155
eslint-plugin/src/rules/member-delimiter-style.ts:276
eslint-plugin/src/rules/no-non-null-assertion.ts:83
eslint-plugin/src/rules/no-unused-vars.ts:272
eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts:228
scope-manager/src/referencer/Referencer.ts:535
typescript-estree/src/convert.ts:986

no-negated-condition (44 occurrences)

docs

Unexpected negated condition.
ast-spec/tests/fixtures.test.ts:210
ast-spec/tests/fixtures.test.ts:314
eslint-plugin/src/rules/consistent-type-assertions.ts:119
eslint-plugin/src/rules/consistent-type-exports.ts:363
eslint-plugin/src/rules/key-spacing.ts:122
eslint-plugin/src/rules/member-ordering.ts:1056
eslint-plugin/src/rules/naming-convention-utils/parse-options.ts:43
eslint-plugin/src/rules/naming-convention-utils/parse-options.ts:47
eslint-plugin/src/rules/naming-convention-utils/parse-options.ts:55
eslint-plugin/src/rules/no-empty-interface.ts:98
eslint-plugin/src/rules/no-non-null-assertion.ts:57
eslint-plugin/src/rules/no-non-null-assertion.ts:101
eslint-plugin/src/rules/no-unnecessary-condition.ts:283
eslint-plugin/src/rules/no-unnecessary-condition.ts:285
eslint-plugin/src/rules/prefer-function-type.ts:126
eslint-plugin/src/rules/prefer-function-type.ts:164
eslint-plugin/src/rules/type-annotation-spacing.ts:44
eslint-plugin/src/rules/type-annotation-spacing.ts:45
eslint-plugin/src/rules/unified-signatures.ts:214
eslint-plugin/src/rules/unified-signatures.ts:216
eslint-plugin/src/rules/unified-signatures.ts:534
eslint-plugin/src/util/getFunctionHeadLoc.ts:40
eslint-plugin/src/util/getMemberHeadLoc.ts:52
eslint-plugin/tests/rules/prefer-optional-chain/base-cases.ts:274
rule-schema-to-typescript-types/src/generateArrayType.ts:47
scope-manager/src/referencer/ExportVisitor.ts:61
scope-manager/src/referencer/Referencer.ts:180
scope-manager/src/referencer/Referencer.ts:343
scope-manager/src/referencer/Referencer.ts:532
scope-manager/src/scope/ScopeBase.ts:356
scope-manager/tests/test-utils/serializers/baseSerializer.ts:34
typescript-estree/src/convert.ts:998
typescript-estree/src/convert.ts:1326
typescript-estree/src/convert.ts:1427
typescript-estree/src/convert.ts:2335
typescript-estree/src/convert.ts:2995
typescript-estree/src/create-program/getWatchProgramsForProjects.ts:336
typescript-estree/src/create-program/shared.ts:59
typescript-estree/tests/lib/parse.test.ts:206
website/src/components/ast/PropertyValue.tsx:70
website/src/components/ast/PropertyValue.tsx:79
website/src/components/inputs/CopyButton.tsx:41
website/src/components/inputs/Dropdown.tsx:24
website/src/components/layout/EditorTabs.tsx:25

no-undef-init (3 occurrences)

docs

Its not necessary to initialize previousRank: number | undefined` to undefined.
eslint-plugin/src/rules/member-ordering.ts:656

Its not necessary to initialize prevNode: TSESTree.Node | undefined` to undefined.
eslint-plugin/src/rules/key-spacing.ts:392

Its not necessary to initialize primitive: number | undefined` to undefined.
eslint-plugin/src/rules/no-redundant-type-constituents.ts:345

no-unreachable-loop (2 occurrences)

docs

Invalid loop. Its body allows only one iteration.
eslint-plugin/src/rules/no-mixed-enums.ts:174
eslint-plugin/src/rules/no-unsafe-return.ts:169

no-unused-expressions (7 occurrences)

docs

Expected an assignment or function call and instead saw an expression.
eslint-plugin/src/rules/strict-boolean-expressions.ts:851
eslint-plugin/src/rules/unified-signatures.ts:502
typescript-estree/tests/lib/convert.test.ts:309
typescript-estree/tests/lib/convert.test.ts:326
typescript-estree/tests/lib/convert.test.ts:327
typescript-estree/tests/lib/convert.test.ts:342
typescript-estree/tests/lib/persistentParse.test.ts:68

no-useless-call (1 occurrence)

docs

Unnecessary .call().
website/src/components/lib/debounce.ts:12

no-useless-computed-key (1 occurrence)

docs

Unnecessarily computed property [MemberExpression[computed=true]] found.
eslint-plugin/src/rules/prefer-find.ts:285

no-useless-concat (4 occurrences)

docs

Unexpected string concatenation of literals.
eslint-plugin/tests/rules/dot-notation.test.ts:242
eslint-plugin/tests/rules/dot-notation.test.ts:243
eslint-plugin/tests/rules/dot-notation.test.ts:282
eslint-plugin/tests/rules/dot-notation.test.ts:283

no-useless-return (12 occurrences)

docs

Unnecessary return statement.
eslint-plugin/src/rules/explicit-module-boundary-types.ts:240
eslint-plugin/src/rules/no-misused-promises.ts:346
eslint-plugin/src/rules/no-unnecessary-condition.ts:403
eslint-plugin/src/rules/prefer-reduce-type-parameter.ts:116
eslint-plugin/src/rules/prefer-return-this-type.ts:115
eslint-plugin/src/rules/prefer-return-this-type.ts:118
eslint-plugin/src/rules/return-await.ts:359
eslint-plugin/src/util/getThisExpression.ts:21
rule-schema-to-typescript-types/src/optimizeAST.ts:51
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:13
typescript-estree/tests/lib/semanticInfo-singleRun.test.ts:16
website/src/components/lib/scroll-into.ts:32

no-var (1 occurrence)

docs

Unexpected var, use let or const instead.
website-eslint/src/mock/path.js:150

no-void (1 occurrence)

docs

Expected undefined and instead saw void.
rule-tester/src/RuleTester.ts:974

object-shorthand (41 occurrences)

docs

Expected method shorthand.
eslint-plugin/src/rules/no-unnecessary-boolean-literal-compare.ts:219
eslint-plugin/src/rules/no-unnecessary-qualifier.ts:179

Expected property shorthand.
eslint-plugin/src/rules/indent.ts:282
eslint-plugin/src/rules/index.ts:177
eslint-plugin/src/rules/index.ts:281
eslint-plugin/src/rules/index.ts:287
eslint-plugin/src/rules/index.ts:296
eslint-plugin/src/rules/no-unsafe-assignment.ts:404
eslint-plugin/src/rules/no-unsafe-call.ts:72
eslint-plugin/src/rules/type-annotation-spacing.ts:60
eslint-plugin/src/rules/type-annotation-spacing.ts:61
eslint-plugin/tests/rules/naming-convention/cases/createTestCases.ts:288
rule-tester/src/utils/validationHelpers.ts:114
typescript-estree/src/convert.ts:2339
typescript-estree/src/convert.ts:2907
typescript-estree/src/convert.ts:3047
typescript-estree/src/parseSettings/createParseSettings.ts:167
website-eslint/src/mock/assert.js:70
website-eslint/src/mock/assert.js:71
website-eslint/src/mock/assert.js:72
website-eslint/src/mock/assert.js:73
website-eslint/src/mock/assert.js:74
website-eslint/src/mock/path.js:220
website-eslint/src/mock/path.js:221
website-eslint/src/mock/path.js:222
website-eslint/src/mock/path.js:223
website-eslint/src/mock/path.js:224
website-eslint/src/mock/path.js:225
website-eslint/src/mock/path.js:226
website-eslint/src/mock/path.js:227
website-eslint/src/mock/path.js:228
website-eslint/src/mock/path.js:229
website/sidebars/sidebar.rules.js:5
website/src/components/ast/DataRenderer.tsx:146
website/src/components/lib/jsonSchema.ts:128
website/src/components/lib/jsonSchema.ts:209
website/src/components/linter/createLinter.ts:69
website/src/components/linter/createLinter.ts:107
website/src/components/linter/createParser.ts:60
website/src/components/linter/createParser.ts:62
website/src/components/linter/utils.ts:98

operator-assignment (1 occurrence)

docs

Assignment (=) can be replaced with operator assignment (+=).
rule-schema-to-typescript-types/src/index.ts:53

prefer-arrow-callback (3 occurrences)

docs

Unexpected function expression.
website-eslint/src/mock/path.js:85
website-eslint/src/mock/path.js:102
website-eslint/src/mock/path.js:127

prefer-const (2 occurrences)

docs

isPathAbsolute is never reassigned. Use const instead.
website-eslint/src/mock/path.js:97

trailingSlash is never reassigned. Use const instead.
website-eslint/src/mock/path.js:98

prefer-object-has-own (11 occurrences)

docs

Use Object.hasOwn() instead of Object.prototype.hasOwnProperty.call().
eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts:58
eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts:62
eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts:66
eslint-plugin/src/rules/no-duplicate-type-constituents.ts:48
eslint-plugin/src/rules/no-restricted-imports.ts:175
eslint-plugin/src/rules/no-restricted-imports.ts:181
rule-tester/src/utils/cloneDeeplyExcludesParent.ts:14
rule-tester/src/utils/freezeDeeply.ts:10
rule-tester/src/utils/serialization.ts:29
type-utils/src/isTypeReadonly.ts:47
typescript-estree/tests/test-utils/test-utils.ts:127

prefer-object-spread (9 occurrences)

docs

Use an object spread instead of Object.assign eg: { ...foo }.
eslint-plugin/src/rules/ban-types.ts:210
eslint-plugin/src/rules/indent.ts:184
eslint-plugin/src/rules/no-extra-parens.ts:306
eslint-plugin/src/util/getFunctionHeadLoc.ts:200
eslint-plugin/src/util/getFunctionHeadLoc.ts:201
parser/tests/test-utils/test-utils.ts:43
parser/tests/test-utils/test-utils.ts:75
rule-tester/src/RuleTester.ts:404
utils/tests/eslint-utils/deepMerge.test.ts:41

prefer-rest-params (2 occurrences)

docs

Use the rest parameters instead of arguments.
website-eslint/src/mock/path.js:67
website-eslint/src/mock/path.js:125

prefer-template (36 occurrences)

docs

Unexpected string concatenation.
ast-spec/tests/util/serialize-error.ts:17
ast-spec/tests/util/snapshot-diff.ts:60
eslint-plugin-internal/src/rules/plugin-test-formatting.ts:66
eslint-plugin/src/rules/consistent-generic-constructors.ts:106
eslint-plugin/src/rules/no-floating-promises.ts:70
eslint-plugin/src/rules/no-floating-promises.ts:72
eslint-plugin/src/rules/no-unsafe-assignment.ts:327
eslint-plugin/src/rules/no-unsafe-assignment.ts:328
eslint-plugin/src/rules/prefer-function-type.ts:147
eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts:348
eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts:358
eslint-plugin/src/rules/unbound-method.ts:107
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:45
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:47
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:50
eslint-plugin/tests/docs.test.ts:86
eslint-plugin/tests/docs.test.ts:90
eslint-plugin/tests/docs.test.ts:100
eslint-plugin/tests/docs.test.ts:455
eslint-plugin/tests/docs.test.ts:461
eslint-plugin/tests/docs.test.ts:469
eslint-plugin/tests/docs.test.ts:475
eslint-plugin/tests/docs.test.ts:485
eslint-plugin/tools/generate-breaking-changes.mts:42
parser/tests/test-utils/test-utils.ts:90
rule-schema-to-typescript-types/src/index.ts:53
typescript-estree/tests/test-utils/test-utils.ts:61
utils/tests/eslint-utils/getParserServices.test.ts:36
website-eslint/build.ts:26
website-eslint/build.ts:33
website-eslint/src/mock/assert.js:54
website-eslint/src/mock/path.js:76
website/src/components/editor/createProvideTwoslashInlay.ts:71
website/src/components/editor/createProvideTwoslashInlay.ts:83
website/src/components/editor/useSandboxServices.ts:88
website/tools/generate-website-dts.ts:56

radix (2 occurrences)

docs

Missing radix parameter.
eslint-plugin/src/util/misc.ts:163
eslint-plugin/tests/schemas.test.ts:172

@abrahamguo abrahamguo added repo maintenance things to do with maintenance of the repo, and not with code/docs triage Waiting for team members to take a look labels Jul 9, 2024
@abrahamguo
Copy link
Contributor Author

prefer-destructuring (454 occurrences)

docs

Use array destructuring.
eslint-plugin-internal/src/rules/no-relative-paths-to-internal-packages.ts:46
eslint-plugin-internal/src/rules/no-relative-paths-to-internal-packages.ts:55
eslint-plugin-internal/src/rules/plugin-test-formatting.ts:59
eslint-plugin-internal/src/rules/plugin-test-formatting.ts:353
eslint-plugin-internal/src/rules/plugin-test-formatting.ts:378
eslint-plugin/src/rules/array-type.ts:263
eslint-plugin/src/rules/consistent-type-exports.ts:198
eslint-plugin/src/rules/consistent-type-imports.ts:538
eslint-plugin/src/rules/consistent-type-imports.ts:546
eslint-plugin/src/rules/consistent-type-imports.ts:548
eslint-plugin/src/rules/consistent-type-imports.ts:550
eslint-plugin/src/rules/consistent-type-imports.ts:559
eslint-plugin/src/rules/consistent-type-imports.ts:562
eslint-plugin/src/rules/no-floating-promises.ts:352
eslint-plugin/src/rules/no-loop-func.ts:193
eslint-plugin/src/rules/no-misused-promises.ts:561
eslint-plugin/src/rules/no-shadow.ts:416
eslint-plugin/src/rules/no-unnecessary-condition.ts:475
eslint-plugin/src/rules/no-unused-vars.ts:194
eslint-plugin/src/rules/no-unused-vars.ts:212
eslint-plugin/src/rules/no-unused-vars.ts:450
eslint-plugin/src/rules/no-use-before-define.ts:193
eslint-plugin/src/rules/padding-line-between-statements.ts:405
eslint-plugin/src/rules/padding-line-between-statements.ts:406
eslint-plugin/src/rules/padding-line-between-statements.ts:407
eslint-plugin/src/rules/padding-line-between-statements.ts:408
eslint-plugin/src/rules/prefer-function-type.ts:104
eslint-plugin/src/rules/prefer-function-type.ts:173
eslint-plugin/src/rules/prefer-includes.ts:231
eslint-plugin/src/rules/prefer-includes.ts:245
eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts:566
eslint-plugin/src/rules/prefer-readonly.ts:307
eslint-plugin/src/rules/prefer-string-starts-ends-with.ts:259
eslint-plugin/src/rules/prefer-string-starts-ends-with.ts:408
eslint-plugin/src/rules/prefer-string-starts-ends-with.ts:660
eslint-plugin/src/rules/prefer-string-starts-ends-with.ts:705
eslint-plugin/src/rules/return-await.ts:89
eslint-plugin/src/rules/return-await.ts:221
eslint-plugin/src/rules/return-await.ts:222
eslint-plugin/src/rules/return-await.ts:228
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:111
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:113
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:158
eslint-plugin/src/rules/use-unknown-in-catch-callback-variable.ts:297
eslint-plugin/src/util/collectUnusedVariables.ts:267
eslint-plugin/tests/rules/indent/utils.ts:11
eslint-plugin/tests/util/getWrappedCode.test.ts:47
repo-tools/src/generate-lib.mts:97
rule-tester/src/utils/SourceCodeFixer.ts:62
rule-tester/src/utils/SourceCodeFixer.ts:63
rule-tester/tests/RuleTester.test.ts:297
scope-manager/tests/eslint-scope/arguments.test.ts:18
scope-manager/tests/eslint-scope/arguments.test.ts:24
scope-manager/tests/eslint-scope/catch-scope.test.ts:22
scope-manager/tests/eslint-scope/catch-scope.test.ts:28
scope-manager/tests/eslint-scope/catch-scope.test.ts:35
scope-manager/tests/eslint-scope/catch-scope.test.ts:41
scope-manager/tests/eslint-scope/catch-scope.test.ts:48
scope-manager/tests/eslint-scope/child-visitor-keys.test.ts:25
scope-manager/tests/eslint-scope/child-visitor-keys.test.ts:49
scope-manager/tests/eslint-scope/class-fields.test.ts:13
scope-manager/tests/eslint-scope/class-fields.test.ts:16
scope-manager/tests/eslint-scope/class-fields.test.ts:25
scope-manager/tests/eslint-scope/class-fields.test.ts:45
scope-manager/tests/eslint-scope/class-fields.test.ts:48
scope-manager/tests/eslint-scope/class-fields.test.ts:63
scope-manager/tests/eslint-scope/class-fields.test.ts:66
scope-manager/tests/eslint-scope/class-fields.test.ts:78
scope-manager/tests/eslint-scope/class-fields.test.ts:81
scope-manager/tests/eslint-scope/class-fields.test.ts:88
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:22
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:29
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:46
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:53
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:75
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:82
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:99
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:106
scope-manager/tests/eslint-scope/es6-arrow-function-expression.test.ts:119
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:20
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:25
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:46
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:51
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:59
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:79
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:86
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:118
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:124
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:130
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:137
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:142
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:149
scope-manager/tests/eslint-scope/es6-block-scope.test.ts:154
scope-manager/tests/eslint-scope/es6-catch.test.ts:26
scope-manager/tests/eslint-scope/es6-catch.test.ts:34
scope-manager/tests/eslint-scope/es6-catch.test.ts:42
scope-manager/tests/eslint-scope/es6-catch.test.ts:55
scope-manager/tests/eslint-scope/es6-class.test.ts:23
scope-manager/tests/eslint-scope/es6-class.test.ts:33
scope-manager/tests/eslint-scope/es6-class.test.ts:43
scope-manager/tests/eslint-scope/es6-class.test.ts:63
scope-manager/tests/eslint-scope/es6-class.test.ts:71
scope-manager/tests/eslint-scope/es6-class.test.ts:81
scope-manager/tests/eslint-scope/es6-class.test.ts:101
scope-manager/tests/eslint-scope/es6-class.test.ts:109
scope-manager/tests/eslint-scope/es6-class.test.ts:117
scope-manager/tests/eslint-scope/es6-class.test.ts:143
scope-manager/tests/eslint-scope/es6-class.test.ts:149
scope-manager/tests/eslint-scope/es6-class.test.ts:160
scope-manager/tests/eslint-scope/es6-class.test.ts:183
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:29
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:35
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:72
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:78
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:117
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:123
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:162
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:168
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:206
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:212
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:251
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:257
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:296
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:302
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:338
scope-manager/tests/eslint-scope/es6-default-parameters.test.ts:342
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:22
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:31
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:62
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:71
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:102
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:112
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:152
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:164
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:208
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:219
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:275
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:289
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:346
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:356
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:397
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:407
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:443
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:452
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:483
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:492
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:521
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:529
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:568
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:579
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:616
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:627
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:675
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:686
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:715
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:724
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:757
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:768
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:794
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:804
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:834
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:845
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:878
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:889
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:923
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:934
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:968
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:978
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:997
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1007
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1036
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1048
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1072
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1084
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1114
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1121
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1156
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1163
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1199
scope-manager/tests/eslint-scope/es6-destructuring-assignments.test.ts:1206
scope-manager/tests/eslint-scope/es6-export.test.ts:18
scope-manager/tests/eslint-scope/es6-export.test.ts:24
scope-manager/tests/eslint-scope/es6-export.test.ts:41
scope-manager/tests/eslint-scope/es6-export.test.ts:47
scope-manager/tests/eslint-scope/es6-export.test.ts:55
scope-manager/tests/eslint-scope/es6-export.test.ts:71
scope-manager/tests/eslint-scope/es6-export.test.ts:77
scope-manager/tests/eslint-scope/es6-export.test.ts:83
scope-manager/tests/eslint-scope/es6-export.test.ts:96
scope-manager/tests/eslint-scope/es6-export.test.ts:102
scope-manager/tests/eslint-scope/es6-export.test.ts:117
scope-manager/tests/eslint-scope/es6-export.test.ts:123
scope-manager/tests/eslint-scope/es6-export.test.ts:140
scope-manager/tests/eslint-scope/es6-export.test.ts:146
scope-manager/tests/eslint-scope/es6-export.test.ts:163
scope-manager/tests/eslint-scope/es6-export.test.ts:169
scope-manager/tests/eslint-scope/es6-export.test.ts:184
scope-manager/tests/eslint-scope/es6-export.test.ts:190
scope-manager/tests/eslint-scope/es6-export.test.ts:202
scope-manager/tests/eslint-scope/es6-export.test.ts:208

Use object destructuring.
ast-spec/tests/util/serializers/Node.ts:53
ast-spec/tests/util/serializers/Node.ts:54
ast-spec/tests/util/serializers/Node.ts:55
eslint-plugin-internal/src/rules/plugin-test-formatting.ts:185
eslint-plugin-internal/src/rules/prefer-ast-types-enum.ts:56
eslint-plugin/src/rules/block-spacing.ts:102
eslint-plugin/src/rules/block-spacing.ts:129
eslint-plugin/src/rules/class-literal-property-style.ts:214
eslint-plugin/src/rules/class-literal-property-style.ts:217
eslint-plugin/src/rules/comma-spacing.ts:60
eslint-plugin/src/rules/consistent-indexed-object-style.ts:100
eslint-plugin/src/rules/consistent-type-imports.ts:252
eslint-plugin/src/rules/consistent-type-imports.ts:270
eslint-plugin/src/rules/dot-notation.ts:73
eslint-plugin/src/rules/dot-notation.ts:75
eslint-plugin/src/rules/explicit-function-return-type.ts:145
eslint-plugin/src/rules/lines-around-comment.ts:156
eslint-plugin/src/rules/member-ordering.ts:1041
eslint-plugin/src/rules/member-ordering.ts:1042
eslint-plugin/src/rules/member-ordering.ts:1043
eslint-plugin/src/rules/method-signature-style.ts:132
eslint-plugin/src/rules/naming-convention-utils/validator.ts:351
eslint-plugin/src/rules/naming-convention.ts:119
eslint-plugin/src/rules/naming-convention.ts:278
eslint-plugin/src/rules/naming-convention.ts:579
eslint-plugin/src/rules/naming-convention.ts:600
eslint-plugin/src/rules/no-empty-function.ts:111
eslint-plugin/src/rules/no-floating-promises.ts:125
eslint-plugin/src/rules/no-floating-promises.ts:128
eslint-plugin/src/rules/no-loop-func.ts:87
eslint-plugin/src/rules/no-magic-numbers.ts:111
eslint-plugin/src/rules/no-shadow.ts:300
eslint-plugin/src/rules/no-shadow.ts:319
eslint-plugin/src/rules/no-shadow.ts:366
eslint-plugin/src/rules/no-shadow.ts:556
eslint-plugin/src/rules/no-unnecessary-condition.ts:212
eslint-plugin/src/rules/no-unnecessary-condition.ts:590
eslint-plugin/src/rules/no-unnecessary-qualifier.ts:28
eslint-plugin/src/rules/no-unsafe-argument.ts:112
eslint-plugin/src/rules/no-unsafe-declaration-merging.ts:34
eslint-plugin/src/rules/no-unsafe-return.ts:195
eslint-plugin/src/rules/padding-line-between-statements.ts:282
eslint-plugin/src/rules/padding-line-between-statements.ts:284
eslint-plugin/src/rules/padding-line-between-statements.ts:782
eslint-plugin/src/rules/prefer-find.ts:86
eslint-plugin/src/rules/prefer-find.ts:160
eslint-plugin/src/rules/prefer-find.ts:289
eslint-plugin/src/rules/prefer-nullish-coalescing.ts:165
eslint-plugin/src/rules/prefer-optional-chain-utils/analyzeChain.ts:343
eslint-plugin/src/rules/prefer-optional-chain-utils/gatherLogicalOperands.ts:155
eslint-plugin/src/rules/prefer-readonly.ts:119
eslint-plugin/src/rules/quotes.ts:43
eslint-plugin/src/rules/space-before-function-paren.ts:85
eslint-plugin/src/rules/space-infix-ops.ts:133
eslint-plugin/src/rules/unbound-method.ts:291
eslint-plugin/src/rules/unified-signatures.ts:366
eslint-plugin/src/util/collectUnusedVariables.ts:140
eslint-plugin/src/util/collectUnusedVariables.ts:262
eslint-plugin/src/util/collectUnusedVariables.ts:428
eslint-plugin/src/util/collectUnusedVariables.ts:566
eslint-plugin/src/util/collectUnusedVariables.ts:652
eslint-plugin/src/util/collectUnusedVariables.ts:685
eslint-plugin/src/util/collectUnusedVariables.ts:701
eslint-plugin/src/util/explicitReturnTypeUtils.ts:139
eslint-plugin/src/util/getFunctionHeadLoc.ts:155
eslint-plugin/src/util/getFunctionHeadLoc.ts:175
eslint-plugin/src/util/getFunctionHeadLoc.ts:177
eslint-plugin/src/util/getFunctionHeadLoc.ts:181
eslint-plugin/src/util/getFunctionHeadLoc.ts:192
eslint-plugin/src/util/getFunctionHeadLoc.ts:193
eslint-plugin/src/util/getFunctionHeadLoc.ts:195
eslint-plugin/src/util/getMemberHeadLoc.ts:40
eslint-plugin/src/util/getMemberHeadLoc.ts:47
eslint-plugin/src/util/getMemberHeadLoc.ts:53
eslint-plugin/src/util/getMemberHeadLoc.ts:59
eslint-plugin/src/util/isAssignee.ts:5
parser/src/index.ts:13
parser/tests/test-utils/test-utils.ts:49
parser/tests/test-utils/test-utils.ts:77
repo-tools/src/generate-sponsors.mts:150
rule-schema-to-typescript-types/src/generateArrayType.ts:69
rule-tester/src/RuleTester.ts:511
rule-tester/src/RuleTester.ts:542
rule-tester/src/RuleTester.ts:656
rule-tester/src/RuleTester.ts:718
rule-tester/src/RuleTester.ts:776
rule-tester/src/utils/SourceCodeFixer.ts:61
scope-manager/src/referencer/ImportVisitor.ts:43
scope-manager/src/referencer/ImportVisitor.ts:50
scope-manager/src/referencer/ImportVisitor.ts:55
scope-manager/src/referencer/Referencer.ts:326
scope-manager/src/referencer/Referencer.ts:399
scope-manager/src/referencer/Referencer.ts:773
scope-manager/src/referencer/TypeVisitor.ts:139
scope-manager/src/scope/ScopeBase.ts:66
scope-manager/src/scope/ScopeBase.ts:74
scope-manager/src/scope/ScopeBase.ts:267
scope-manager/src/scope/ScopeBase.ts:284
scope-manager/src/scope/ScopeBase.ts:298
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:16
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:38
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:62
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:85
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:109
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:130
scope-manager/tests/eslint-scope/implicit-global-reference.test.ts:154
scope-manager/tests/eslint-scope/references.test.ts:532
scope-manager/tests/lib.test.ts:10
scope-manager/tests/lib.test.ts:19
type-utils/src/getContextualType.ts:12
type-utils/tests/isTypeReadonly.test.ts:24
type-utils/tests/isTypeReadonly.test.ts:25
typescript-estree/src/convert.ts:43
typescript-estree/src/convert.ts:343
typescript-estree/src/index.ts:26
typescript-estree/src/node-utils.ts:11
utils/src/ts-eslint/Scope.ts:12
utils/src/ts-eslint/Scope.ts:16
website/plugins/generated-rule-docs/insertions/insertNewRuleReferences.ts:197
website/src/components/ast/DataRenderer.tsx:221
website/src/components/hooks/useHashState.ts:152
website/src/components/hooks/useHashState.ts:158
website/src/components/hooks/useHashState.ts:164
website/src/components/lib/createCompilerOptions.ts:27
website/src/theme/MDXComponents/RuleAttributes.tsx:38

and 254 more

@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented Jul 10, 2024

I'm guessing a fair amount can be pruned from this list.... At a glance,

  • default-parameters-last - @typescript-eslint/default-parameters-last should be used instead, which currently has no violations (not sure whether that's a coincidence or because it's already enabled everywhere - hard to tell from flat config 🙃).
  • things affecting the website-eslint/src/mock directory (like no-var) - not sure these are really intended to be linted to modern standards... though perhaps it wouldn't hurt. See chore(website): add playground to website #4108
  • prefer-destructuring - I am -1 on this rule in general. And especially -1 with the options that report const foo = arr[2] since it could be written const [_unused_1, _unused_2, foo] = arr (which I note is at least the first several of the 454 violations).

But, I'm sure there are some that would make sense to enable!

@Josh-Cena
Copy link
Member

Josh-Cena commented Jul 10, 2024

with the options that report const foo = arr[2] since it could be written const [_unused_1, _unused_2, foo] = arr

It would be const [, , foo] = arr.

@kirkwaiblinger
Copy link
Member

It would be const [, , foo] = arr.

🥴

@Josh-Cena
Copy link
Member

Josh-Cena commented Jul 10, 2024

I'm +1 on the following rules:

  • array-callback-return
  • arrow-body-style
  • no-await-in-loop (in principle; it looks like it's not too helpful in practice)
  • no-lonely-if
  • no-unreachable-loop (not sure if existing ones are bugs)
  • no-useless-call
  • no-useless-computed-key
  • no-useless-concat
  • no-useless-return
  • no-var
  • no-void
  • operator-assignment
  • prefer-arrow-callback
  • prefer-const
  • prefer-object-has-own
  • prefer-object-spread
  • prefer-rest-params
  • radix

I'm +0.5 on the following rules:

  • no-unused-expressions (with our extended version)
  • object-shorthand (but needs to be disabled in some places)
  • prefer-template
  • prefer-destructuring (need some tweaking so it's less strict)

I'm -1 on the following rules:

  • class-methods-use-this
  • no-negated-condition
  • no-undef-init

@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented Jul 10, 2024

I basically agree with @Josh-Cena.

Only notable differences in opinion are

  • prefer-destructuring (base or extension), -1 from me
  • @typescript-eslint/class-methods-use-this, +0.5 (mostly for dogfooding reasons)

Note that, in my experience, prefer-template is good, but not if you just interpolate arbitrary expressions into the template in order to fix the report. Much better is to create a named variable for the expression and interpolate that.

@Josh-Cena
Copy link
Member

but not if you just interpolate arbitrary expressions into the template in order to fix the report.

How is that different from using + to join arbitrary expressions?

@Josh-Cena
Copy link
Member

Josh-Cena commented Jul 10, 2024

If it interests you: I have written down my opinion on every ESLint rule here: https://jc-verse.github.io/js-style-guide/eslint-base/ ;) (WIP)

@abrahamguo
Copy link
Contributor Author

@Josh-Cena very cool! love that

@bradzacher
Copy link
Member

For all the stylistic rules - throw them all away straight away - unless they have a fixer for all cases. Why? Migration effort for starters. Maintenance burden (eg copying in forked code). Contributor pains (having to manually adjust their natural code style to meet our preference).


I'm a -1 on prefer-destructuring. I don't believe that always destructuring is good. I only ever reach for destructuring when I have 2 or more properties, personally.
I dislike the always style cos it doesn't play nice with optional chaining. Eg const bar = foo?.bar; has no destructuring form - so you end up either having these "almost destructurable" cases in the codebase or you have to do like const {bar} = foo ?? {}; (ew).

I'm a -1 on no-undef-init too, personally. I like the explicitness of assigning undefined initially sometimes. It helps the code read better - eg "this variable starts with undefined and then is assigned foo later". It feels better to explicitly initialise the variable.

-1 on array-callback-return as it isn't type-aware so it can't understand exhaustive logic.

-1 on arrow-body-style - I don't believe that "always using implicit return" is a good style, personally. Sometimes you can get cleaner code and / or better formatting by adding the return.

-1000 on no-negated-condition. The premise of that rule is just plain wrong. There's absolutely nothing wrong with if (x != null) {...} else {...} - the code would be no clearer with if (x == null) as the condition -- in fact in many cases it could be less clear.

-0.1 on no-unreachable-loop. It's not great style but it can be a convenient way to write code that is "do this logic on the first element if there is one" as opposed to writing a length check and manually extracting the first element. Also not all iterators can be indexed.

-1 on no-useless-return. It can be a nice style to explicitly have a useless return to document a case. For example the first violation you list is exactly that - a "useless return" that exists to enumerate the switch cases and document why its purposely not handled.

-0.5 on prefer-object-spread - sometimes the Object.assign is better to work around inferred types.

@kirkwaiblinger
Copy link
Member

How is that different from using + to join arbitrary expressions?

Good question - took me a minute to remember when and why I formed this opinion. The reason is I was working in a project where we had lots of code roughly like this

/** translates a string to the current lang */
declare function $t(s: string): string;
/** interact with the user in some way */
declare function promptUser(s: string): void;

promptUser($t("This is a string to be translated.") + " " + $t("Do you find this easy to read in a diff without syntax highlighting?"));

// after fix

promptUser(`${$t("This is a string to be translated.")} ${$t("Do you find this easy to read in a diff without syntax highlighting?")}`);

// extracted to variables

const thisIsAStringToBeTranslated = $t("This is a string to be translated.");
const canUReadIt = $t("Do you find this easy to read in a diff without syntax highlighting?");

promptUser(`${thisIsAStringToBeTranslated} ${canUReadIt}`);

IMO the second variant ("after fix") is by far the least readable, and it's really hard to notice the space in the middle compared to the other two forms.

In retrospect, I think this readability pitfall might have been pretty specific to that codebase due to the $t looking similar to ${ and the manually manually inserted space. So, unless something like this comes up when fixing the errors, nothing to stress about.

@Josh-Cena
Copy link
Member

Josh-Cena commented Jul 10, 2024

I dislike the always style cos it doesn't play nice with optional chaining. Eg const bar = foo?.bar; has no destructuring form - so you end up either having these "almost destructurable" cases in the codebase or you have to do like const {bar} = foo ?? {}; (ew).

The rule doesn't report on this. It also doesn't report on const bar: T = foo.bar.

I think the rule works very nicely with object properties and I would recommend. The only reservations I have are with array indices, where the reports are neither always better nor always correct (it transforms code using array-like protocol to iterable protocol).

-1 on array-callback-return as it isn't type-aware so it can't understand exhaustive logic.

Exhaustiveness should be asserted or with a fallback guard; but I agree it's less useful in TS because TS also checks this.

-1 on arrow-body-style - I don't believe that "always using implicit return" is a good style, personally. Sometimes you can get cleaner code and / or better formatting by adding the return.

Disagree—it increases code entropy for no clear reason. I'm in the camp that "if you remove something, you must remove all other extra syntax that was necessitated by it until your code is reduced to absolute minimum".

-0.1 on no-unreachable-loop. It's not great style but it can be a convenient way to write code that is "do this logic on the first element if there is one" as opposed to writing a length check and manually extracting the first element. Also not all iterators can be indexed.

You should definitely do const [first] = iterable then. for (const first of iterable) and array destructuring both use the iterator protocol and they are exactly equivalent. It's not nice to have others question if something is working properly or if it's just a hidden bug.

-1 on no-useless-return. It can be a nice style to explicitly have a useless return to document a case. For example the first violation you list is exactly that - a "useless return" that exists to enumerate the switch cases and document why its purposely not handled.

I agree that sometimes it's good to be defensive and preemptively terminate all possible branches, but I also think it's extra syntax to create confusion.

-0.5 on prefer-object-spread - sometimes the Object.assign is better to work around inferred types.

Well, either fix the types or assert? Object.assign is less ergonomic/sound with types due to it using intersections.

@abrahamguo
Copy link
Contributor Author

I do see @bradzacher's points, but tend to agree most with @Josh-Cena here.

At any rate, I'll open some draft PRs for some of these so we can start seeing what these individual rule changesets look like.

@bradzacher
Copy link
Member

const bar = foo?.bar;
[prefer-destructuring] doesn't report on this. It also doesn't report on const bar: T = foo.bar.

Which is my point! My point is that if I write this code:

const bar = foo.bar;
const bar = foo?.bar;
const bar: T = foo.bar;

Then the rule would force me to instead write

const { bar } = foo;
const bar = foo?.bar;
const bar: T = foo.bar;

And now I've got inconsistently styled code.
I'd personally rather we have no rule and let people do what they want rather than a rule that enforces an inconsistent style.

@Josh-Cena
Copy link
Member

Ummm... Sure? People can also opt into const { bar } = foo ?? {} as well. The point is that destructure-by-default is more extensible and less repetitive, and whether you want to use destructuring for other cases is up to you.

@bradzacher
Copy link
Member

You say

It can be a nice style to explicitly have a useless return to document a case.
I agree that sometimes it's good to be defensive and preemptively terminate all possible branches, but I also think it's extra syntax to create confusion.

And you also say

Exhaustiveness should be asserted or with a fallback guard

So is the exhaustive pre-emption confusing extra syntax, or is it required?


The case I specifically mentioned was this, which is the last statement in a function:

https://github.com/typescript-eslint/typescript-eslint/blob/main/packages%2Feslint-plugin%2Fsrc%2Frules%2Fexplicit-module-boundary-types.ts#L220-L241

I think we've got our exhaustive switch rule turned on in our codebase, don't we?

The rule reports on the last case's return - saying its useless. It's not wrong but also the options are:

  1. Remove it - bad because it's good to be exhaustive and violates the exhaustive rule.
  2. Delete just the return - bad IMO because having an empty case at the end looks like a bug.

It's just an example of where the extra syntax isn't noise and it isn't confusing. TBH it took me a second to realise why the rule reported on it - cos the function decl header is many lines above and wasn't on my screen - so all I could see was the closing curlies.
Having the return made it clear "this case does not do anything ever because it just has a return statement".

It's why I'm a strong -1. I'd even bump it to a -2 on no-useless-return. It's a style that's not harmful and can even help clarify code.

@Josh-Cena
Copy link
Member

Josh-Cena commented Jul 11, 2024

This won't appear like a bug to anyone and is there to be future-proof:

switch (x) {
  case ...:
  default:
    throw new Error("Unreachable");
}

This is unnecessary syntax, is trivially refactorable, but isn't likely a bug:

doSomething(() => {
  return ...
});

This is unnecessary syntax, and is likely a bug:

for (const x of y) {
  ...
  return;
}

This is unnecessary syntax, could be preventing bugs, but the bug vector is unclear and more implicit, because it isn't meant to catch effect-at-a-distance kind of bug like switch exhaustiveness check does.

function x() {
  if (...) {
    ...
    return;
  } else {
    ...
    return;
  }
}

@bradzacher
Copy link
Member

bradzacher commented Jul 11, 2024

My point is that the rule flags this:

switch (v) {
  case a:
    return;

  case b:
    return; // flagged as unnecessary
}

My argument is that this is technically unnecessary but it:

  • provides visual symmetry, which is nice when comparing cases
  • provides clear and explicit termination of the branch
  • makes code refactor-proof so I needn't add a return if I add a new case.

Eg

 case b:
+  return;
+
+case c:

Vs

 case b:
  return;
+
+case c:
+  return;

Smaller diffs are nicer.

I just don't personally see any positives with deleting a return, personally. But I see negatives.

We can agree to disagree here - I'm still a -1 on the rule.

@Josh-Cena
Copy link
Member

Practically, in this case, I would suggest changing it to break, but I agree that the rule suggesting to remove return in this case is wrong.

@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented Jul 12, 2024

Summary

(Someone please yell if any of these are wrong)

Accepted

Sounds like there is no objection for

And sufficient consensus to proceed if someone wants to champion

Rejected/vetoed

  • array-callback-return, +1 from Josh Cena, -1 from Brad, -0.5 from me.
  • arrow-body-style, +1 from Josh Cena, -1 from Brad, -0.5 from me.
  • no-useless-return: +1 from Josh Cena, -1/-2 from Brad, -1 from me.
  • prefer-destructuring: +0.5 from Josh Cena, -1 from Brad, -1 from me.
  • @typescript-eslint/class-methods-use-this: -1 from Josh Cena, +0.5 from me, -0.5 from Brad

EDIT - moved some around to reflect #9523 (comment)

@bradzacher
Copy link
Member

bradzacher commented Jul 12, 2024

I'm personally -0.5 on @typescript-eslint/class-methods-use-this.
I don't agree that all class methods must use this. It can be useful to extract functionality into a this-less function on the class for co-location.

The rule bans that pattern and forces you to move the function outside the class entirely as a standalone declararion - which I disagree with, personally.


no-lonely-if I'm indifferent towards.

@Josh-Cena Josh-Cena added accepting prs Go ahead, send a pull request that resolves this issue and removed triage Waiting for team members to take a look labels Jul 12, 2024
@abrahamguo
Copy link
Contributor Author

Doesn't class-methods-use-this just want you to change those methods to static?

@kirkwaiblinger
Copy link
Member

kirkwaiblinger commented Jul 12, 2024

Looks like we have a plan of action! 🙂

I would just like to remind implementers of PRs for this issue (i.e. @abrahamguo 🙂) to pls double check for typescript-eslint extension rules for any of these. Might have missed some in the list.

@Josh-Cena
Copy link
Member

Doesn't class-methods-use-this just want you to change those methods to static?

That's an API change and if you look at the reports I don't think they make sense.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
accepting prs Go ahead, send a pull request that resolves this issue repo maintenance things to do with maintenance of the repo, and not with code/docs
Projects
None yet
Development

No branches or pull requests

4 participants