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

[recommended] Remove stylistic rules from recommended #651

Closed
jacobrask opened this issue Jun 26, 2019 · 19 comments · Fixed by #729
Closed

[recommended] Remove stylistic rules from recommended #651

jacobrask opened this issue Jun 26, 2019 · 19 comments · Fixed by #729
Labels
breaking change This change will require a new major version to be released has pr there is a PR raised to close this package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin recommended-rules Discussion about recommended rule sets
Milestone

Comments

@jacobrask
Copy link

jacobrask commented Jun 26, 2019

Stylistic rules can be very useful to set a standard that's consistent in your code base to help people learning and move between projects, but they make it a more difficult to transition an existing code base, and also makes TypeScript less approachable to beginners. I've seen StackOverflow questions with people writing "Please help me fix this TypeScript issue, I can't commit!" when it's actually just a stylistic lint error.

For this reason (I assume), the default eslint:recommended config is quite slim, and it would be nice if @typescript-eslint follows a similar standard for what's recommended.

Even just following the current documentation for the recommended rules would be a nice start 😃

  1. They help you adhere to TypeScript best practices.
  2. They help catch probable issue vectors in your code.

Stylistic rules

I think these rules should be removed from recommended with the 2.0 release. They do not have any effect on the runtime behaviour or type checking of your code (e.g. do not catch issue vectors in your code, and I would not consider something like specific indentation to be a TypeScript best practice)

  • @typescript-eslint/array-type
  • @typescript-eslint/camelcase
  • @typescript-eslint/class-name-casing
  • @typescript-eslint/indent
  • @typescript-eslint/type-annotation-spacing
  • @typescript-eslint/interface-name-prefix
  • @typescript-eslint/member-delimiter-style
  • @typescript-eslint/no-inferrable-types

"Best practices"

These could be considered best practices by some, but are still quite opinionated so it would be nice to take 2.0 as an opportunity to consider if all rules really are suitable for recommended.

  • @typescript-eslint/explicit-member-accessibility - I personally use this in most projects, but accessibility is optional in TypeScript for a reason so it's more of a matter of preference and opinion rather than a general "TypeScript best practice"
  • @typescript-eslint/explicit-function-return-type - Setting allowTypedFunctionExpressions: true as default will help, but it is very unergonomic to use right now.
  • @typescript-eslint/no-array-constructor - corresponding rule is not enabled in eslint-recommended
  • @typescript-eslint/no-non-null-assertion - the ! operator is a useful escape hatch, having to workaround it leads to a lot more code that is more difficult to understand
  • @typescript-eslint/no-object-literal-type-assertion

I hope that a change like this would A) help drive adoption of the project and the very important and relevant rules that would still be left in recommended, B) avoid driving away beginners that just select "defaults" / "recommended" from the TypeScript ecosystem by overly strict rules that are mostly relevant in some specific code bases.

Thanks!

@jacobrask jacobrask added package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin triage Waiting for maintainers to take a look labels Jun 26, 2019
@bradzacher bradzacher added recommended-rules Discussion about recommended rule sets and removed triage Waiting for maintainers to take a look labels Jun 26, 2019
@bradzacher
Copy link
Member

bradzacher commented Jun 27, 2019


  "@typescript-eslint/adjacent-overload-signatures": "error",
     style rule.
     nobody has complained, so I think it's staying.

- "@typescript-eslint/array-type": "error",
-    style rule. settings are very opinionated. should be removed

+ "@typescript-eslint/await-thenable": "error",
+    best practice, should be added.

+ "@typescript-eslint/ban-ts-ignore": "error",
+    best practice, definitely should be added.

  "@typescript-eslint/ban-types": "error",
     best practice, definitely staying.

  "@typescript-eslint/camelcase": "error",
     style rule.
     It is best practice to enforce some naming convention (eslint doesn't have a snake_case rule).
     Most codebases follow this style.
     Nobody has complained, so I think it's staying.

  "@typescript-eslint/class-name-casing": "error",
     style rule.
     It is best practice to enforce some naming convention.
     Most codebases follow this style.
     Nobody has complained, so I think it's staying.

  "@typescript-eslint/explicit-function-return-type": "warn",
     I would argue this isn't a style rule.
     We need to turn allowTypedFunctionExpressions on by default.
     IMO should stay in.
     
- "@typescript-eslint/explicit-member-accessibility": "error",
-    As per #201, this will be removed.
-    I like it and use it to force devs to think about the exposed interface, but I can understand that a lot of people don't like it and just default to `public` to shut the rule up (defeating the purpose).

- "@typescript-eslint/indent": "error",
-    If only because the maintenance on this rule is a nightmare, this should go.

  "@typescript-eslint/interface-name-prefix": "error",
     this will not be going.
     It's not a stylistic thing.
     It is an antipattern in typescript that people should not be using.
     There's literally no reason to prefix interfaces with `I`.
     I will continue to provide reasonable rebuttals against removing this till the day I die.
     See #374.

  "@typescript-eslint/member-delimiter-style": "error",
     style rule.
     I think this is good as it at least enforces some form of standard (otherwise I've seen codebases turn into a lawless wasteland).
     nobody has complained, so I think it's staying.

  "@typescript-eslint/no-angle-bracket-type-assertion": "error",
     best practice, definitely staying.

  "@typescript-eslint/no-array-constructor": "error",
     best practice.
     `new Array(1,2,3)` vs `new Array(1)` is very ambiguous and can cause bugs.
     this is definitely staying.

+ "@typescript-eslint/no-empty-function": "error",  
+    style rule.
+    best practice to avoid unused/useless code.
+    I think this should be added.

  "@typescript-eslint/no-empty-interface": "error",
     style rule.
     best practice to avoid unused/useless code.
     nobody has complained, so I think it's staying.

  "@typescript-eslint/no-explicit-any": "warn",
     best practice to avoid writing unsafe code.
     definitely staying.
     should stay as a warn so it's not build blocking

+ "@typescript-eslint/no-for-in-array": "error",
+    best practice.
+    I think it should be added.

  "@typescript-eslint/no-inferrable-types": "error",
     best practice.
     avoids writing redundant code.
     nobody has complained, so I think it's staying.

  "@typescript-eslint/no-misused-new": "error",
     best practice to avoid writing incorrect code.
     definitely staying.

+ "@typescript-eslint/no-misused-promises": "error",
+    helps avoid unintentional bugs.
+    I think it should be added.

  "@typescript-eslint/no-namespace": "error",
     best practice to avoid writing code using old features.
     definitely staying.

- "@typescript-eslint/no-non-null-assertion": "error",
+ "@typescript-eslint/no-non-null-assertion": "warn", 
     best practice to avoid writing unsafe code.
     definitely staying, but should probably be a warning instead of a build blocking error.

  "@typescript-eslint/no-object-literal-type-assertion": "error",
     best practice to avoid writing unsafe code.
     explicit `as X` casts follow much looser rules than declaration types.
     nobody has complained, so I think it's staying.

- "@typescript-eslint/no-parameter-properties": "error",
-    style rule. very opinionated. should be removed.

+ "@typescript-eslint/no-this-alias": "error",
+    best practice.
+    there should be no reason to alias this with the currently available language features.
+    I think it should be added.

- "@typescript-eslint/no-triple-slash-reference": "error",
-    deprecated - replaced by triple-slash-reference
-    definitely staying.

+ "@typescript-eslint/no-unnecessary-type-assertion": "error",
+    best practice.
+    helps ensure you don't have redundant casts and non-null-assertions.
+    I think it should be added.

  "@typescript-eslint/no-unused-vars": "warn",
     I'm going to leave this because with #688 it should have a much better UX.

- "@typescript-eslint/no-use-before-define": "error",
-    stylistic in terms of function declarations.
-    variable case is caught by typescript compiler.
-    should be removed.

  "@typescript-eslint/no-var-requires": "error",
     best practice.
     esp with `import =` statements, there are few cases to use var requires.
     nobody has complained, so I think it's staying.

+ "@typescript-eslint/prefer-includes": "error",
+    best practice. has a fixer. Array#includes is supported in everything but IE.
+    I think it should be added

- "@typescript-eslint/prefer-interface": "error",
-    style rule. very opinionated. should be removed.

  "@typescript-eslint/prefer-namespace-keyword": "error",
     best practice.
     nobody has complained, so I think it's staying.

+ "@typescript-eslint/prefer-regexp-exec": "error",
+    best practice.
+    I think it should be added

+ "@typescript-eslint/prefer-string-starts-ends-with": "error",
+    best practice. has a fixer. methods are supported in everything but IE.
+    I think it should be added

+ "@typescript-eslint/require-await": "error",
+    best practice.
+    I think it should be added

+ "@typescript-eslint/restrict-plus-operands": "error",
+    best practice.
+    i think it should be added.

+ "@typescript-eslint/triple-slash-reference": "error",
+    replaces no-triple-slash-reference. Best practice.
+    definitely staying.

- "@typescript-eslint/type-annotation-spacing": "error",
-    style rule. very opinionated. should be removed.

+ "@typescript-eslint/typedef": "error",
+    best practice.
+    I think it should be added.

+ "@typescript-eslint/unbound-method": "error",
+    best practice.
+    I think it should be added.

Rules I'm not sure about:

Just to complete the list... rules that aren't in recommended and won't be added:

  • consistent-type-defs - too opinionated..
  • func-call-spacing - style rule
  • generic-type-naming - style rule
  • member-naming - style rule
  • member-ordering - style rule
  • no-extra-parens - style rule
  • no-extraneous-class - style rule
  • no-magic-numbers - style rule
  • no-require-imports - blocks import x = require('x'), so might be too restrictive?
  • no-type-alias - will be deprecated by consistent-type-defs, also style rule.
  • no-unnecessary-qualifier - style rule
  • prefer-for-of - stylistic choice, and has no fixer so it's hard to correct.
  • prefer-function-type - stylistic rule
  • prefer-readonly - stylistic rule
  • promise-function-async - I think this is a bit opinionated?
  • require-array-sort-compare - style rule
  • restrict-plus-operands - some people find this controversial and like to use string + number.
  • semi - style rule
  • strict-boolean-expressions - I think this is too new, and too restrictive to recommend at this point in time. I can definitely see this being added in the next recommended changes.
  • unified-signatures - style rule

@bradzacher bradzacher mentioned this issue Jun 28, 2019
14 tasks
@bradzacher bradzacher added the breaking change This change will require a new major version to be released label Jun 28, 2019
@bradzacher bradzacher added this to the 2.0.0 milestone Jun 28, 2019
@aladdin-add
Copy link
Contributor

adding a rule: prefer-const -- it was in @typescript-eslint/eslint-recommended, but not in eslint:recommended.

@bradzacher
Copy link
Member

@aladdin-add - non-plugin rules are a separate thing.
Will be moving those items from eslit-recommended - see #595

@brainkim
Copy link
Contributor

brainkim commented Jul 3, 2019

Can we add no-explicit-any to the list of “best practices”? I think it’s similar to no-non-null-assertion in that it’s valid typescript and disallowing it is an opinion on typescript best practices, when really there are valid use-cases for having the any escape hatch in typescript.

@glen-84
Copy link
Contributor

glen-84 commented Jul 24, 2019

no-type-alias - will be deprecated by consistent-type-defs, also style rule.

Will this be marked as deprecated in the rule itself?

@bradzacher
Copy link
Member

It won't be because it's not being deprecated by the rule.
I wasn't thinking correctly when I wrote that - no-type-alias doesn't do what I was thinking at the time. I was thinking that it did a similar thing to prefer-interface, but it doesn't - it just enforces that you don't do certain things with your type aliases.

consistent-type-defs enforces that you either use type or interface for object types.

@phaux
Copy link
Contributor

phaux commented Jul 24, 2019

I would like to share my thoughts on some of the rules

Rules I turn off

  • (All the rules that are currently considered for removal)

  • @typescript-eslint/no-use-before-define

    • I actually prefer the exact opposite, ie. to always define function after all it's uses, as long as that's possible
    • I would consider it a stylistic rule

Rules I turn on

  • @typescript-eslint/member-ordering

    • Putting fields above methods is something that everybody does anyways
    • It's nice to have it enforced just in case
  • @typescript-eslint/no-floating-promises

    • It's super useful when it catches these few cases where you forget an await
    • It also makes you think more about error handling
    • You can always silence false-positives with just .catch and empty function or console.error
      • In such case it makes it immediately obvious that this operation is async and you just don't want to await it
  • @typescript-eslint/no-for-in-array

    • I can't think of a valid use case to use for-in on an array
    • You can always use regular for loop or array.keys() if you want to iterate on indices
    • Every time I saw someone do this, he/she admitted it was a mistake
  • @typescript-eslint/prefer-readonly

    • It's very useful for other developers to immediately see that a particular variable is never meant to be modified
    • The only downside to me is that it's not standard javascript syntax, and it's less known
  • @typescript-eslint/restrict-plus-operands

    • Obviously a good thing, makes things more explicit
    • Makes it immedaitely clear for other developers if you mean string concat or addition
    • To silence it, just wrap an operand with either Number(x) / +x or String(x) / `${x}`
  • @typescript-eslint/strict-boolean-expressions

    • Again, makes things more obvious
    • I think it should be slightly relaxed in recommended config to at least ignore boolean operator RHS for some common patterns

@bradzacher
Copy link
Member

@typescript-eslint/no-use-before-define

It's stylistic, but everyone seems to be okay with it.
Might be worth removing the rule because the errors that should be caught by this should be caught by typescript instead.

@typescript-eslint/member-ordering

Ordering is just too opinionated to turn on as recommended. The number of configuration options alone show how opinionated it can be!

@typescript-eslint/no-floating-promises

Yeah I haven't had a chance to use this yet, so I don't have an opinion on it yet!
I was hoping people would chime in more here considering the number of people who have lambasted us in the past for our recommended rule decisions, but you're the first!

If people think there's value in this rule, then I'd definitely turn it on recommended.

@typescript-eslint/prefer-readonly

Most of the typescript code I write now a days is functional in some way, so I don't know how valuable this is to people. Happy to turn it on if there's consensus that it is valuable.

@typescript-eslint/strict-boolean-expressions

Definite value this rule, but it's only just been released AND it hasn't got any options yet. We're planning on having a shorter iteration time for major releases, so this will at least be turned on with the next breaking.

@typescript-eslint/no-for-in-array
@typescript-eslint/restrict-plus-operands

These are set to be turned on!

@glen-84
Copy link
Contributor

glen-84 commented Jul 25, 2019

@phaux,

I actually prefer the exact opposite, ie. to always define function after all it's uses, as long as that's possible

There's an option for that though (functions: false).

@bradzacher,

Might be worth removing the rule because the errors that should be caught by this should be caught by typescript instead.

Apparently not all of them.

@bradzacher
Copy link
Member

Apparently not all of them.

That case is actually a weird error and not error. It's kind of correct for the compiler to not to flag it compiler.

This example is a runtime error because variable is defined, but not yet initialised

const consumer = () => console.log(variable);
consumer();
const variable = 'foo';

However this case is not, because the variable is both defined, and initialised

const consumer = () => console.log(variable);
const variable = 'foo';
consumer();

So in the first example, the rule will fire, and you will be alerted to a runtime bug.
However in the second example, the rule will fire, and you will be alerted to a non-issue.

The important detail is where the usage is, not where the definition is.

@glen-84
Copy link
Contributor

glen-84 commented Jul 26, 2019

Right, but my point was that users might still want to use the rule for these cases.

@phaux
Copy link
Contributor

phaux commented Jul 26, 2019

Also note that the original eslint recommended rules also don't include no-use-before-define.

@jacobrask
Copy link
Author

jacobrask commented Aug 2, 2019

I think I've already given my 2 cents, but the argument to keep formatting/casing rules here seems to be it's a "Best practice" and "Nobody has complained" but the readme explicitly discourages feedback about subjective rules like this:

If you feel very, very, very strongly that a specific rule should (or should not) be in the recommended ruleset, please feel free to file an issue along with a detailed argument explaning your reasoning. We expect to see you citing concrete evidence supporting why (or why not) a rule is considered best practice. Please note that if your reasoning is along the lines of "it's what my project/company does", or "I don't like the rule", then we will likely close the request without discussion.

I guess I simply don't think it's TypeScript's or a TypeScript specific eslint config's concern which best practices regarding casing I follow, even if I prefer to enforce it it most projects.

Would you be open to having one recommended ruleset with the bare minimum of things like avoiding deprecations (using the same philosophy as eslint:recommended and matching the naming convention) and another best-practices which is more opinionated?

(Really hope I don't come across as hostile or negative by the way, this is a great project and differences in opinions here is normal and happens all the time, just trying to include another perspective in the discussion 👍)

@bradzacher
Copy link
Member

Really hope I don't come across as hostile or negative by the way, this is a great project and differences in opinions here is normal and happens all the time, just trying to include another perspective in the discussion 👍

I never assume ill intent. I appreciate everyone giving their feedback!


The reason behind that big paragraph in the config readme is because when we implemented the recommended set, we got a lot of people who raised issues about the recommended set, and they essentially just said "I don't think this rule should be recommended because I don't use it, the end". Which (a) just causes noise and spam for everyone watching the repo, and (b) isn't a good argument at all, right?
How are we supposed to make decisions about whether or not a rule should be considered recommended based on that? The intention is that if you strongly feel that a rule shouldn't be recommended, you should come to us with a well-reasoned argument.
You don't like the rule? That's awesome! But tell us why. What don't you like about it? Is it because you think it goes against the spirit of typescript? Is it because you're just used to a different convention? Do you think it's encourages bad patterns? etc.
These are the discussions we want to foster, and arguments we want people to put forward. But we found that people who raised those short issues didn't want to have a discussion - they just want to drop their opinion and leave.

Since adding that readme, we've essentially received zero issues that have that non-argument. Instead we've received a number of well thought out requests along the lines of "I don't think explicit-member-accessibility should be recommended because of these reasons".

This gives the community something to chime in on and discuss, and it gives us, the maintainers, something to think about and consider. There are a few rules which are being removed because of these discussions.

The wording in that paragraph is probably a bit strong, as I was writing it when I was pissed off about getting another issue, but the points within it are valid. I'm hesitant to change it because I don't want to encourage too much discussion about the recommended set. It's such a teeny, tiny fraction of this project, yet it already takes up such a huge amount of my time.


Our stance with the recommended set is to give people the best practices by default. This includes rules which are considered "stylistic" rules. The best practice and standards within the community include some stylistic decisions like naming for classes/variables.

We want it to be setup such that a user that is new to typescript can install eslint + our plugin, drop the recommended set in, and instantly have best practices enforced on their codebase.

Ultimately it's very easy to adjust the config if you like a different convention.
I mean.. that could be that people are doing with the stylistic rules, meaning we are really wrong in having them recommended... But without any hard data, it's hard to say - hence "Nobody has complained".

We could definitely add an additional, looser recommended config. A number of people have suggested it, but nobody has wanted to do the work to create it.

@brainkim
Copy link
Contributor

brainkim commented Aug 2, 2019

We could definitely add an additional, looser recommended config. A number of people have suggested it, but nobody has wanted to do the work to create it.

Isn’t this what @typescript-eslint/eslint-recommended does (#488)? My personal issue with the “recommended” ruleset is naming. I don’t care that there’s a more opinionated typescript eslint config out there, what I do care is that the official typescript-eslint repository decided to name this opinionated ruleset “recommended” when it goes so far as to provide rules which directly contravene the ruleset provided by eslint-recommended (@typescript-eslint/indent, @typescript-eslint/camelcase).

I think the eslint-recommended ruleset is remarkable in that for the most part, I never disable its rules except via blanket config extensions like eslint-config-prettier. Rules like disallowing switch/case fallthrough or duplicate object keys catch bugs and probably have disable rates of <1% of eslint-recommended installations.

I think that by calling the more opinionated, stylistic rules “recommended,” this repository contradicts the spirit of the eslint-recommended ruleset, whose rules almost always catch bugs and are seldom disabled. I get that as creators and maintainers of the repository, which certainly involves a lot of work, you want to exert some influence over the stylistic conventions adopted by the typescript community, but, in my opinion, calling your opinionated ruleset “recommended” violates the eslint-recommended “trademark” so to speak, and misleads users.

@bradzacher
Copy link
Member

Are you using @typescript-eslint/eslint-recommended and eslint-recommended interchangeably, or when you say eslint-recommended, are you referring to eslint's recommended set?

I think I'm misunderstanding parts of your post because I'm not sure which one you're using.


I think you've misunderstood the intention behind our eslint-recommended config.

It is not a "looser set" of rules - if it was, then it would turn on a bunch of our rules, which it clearly doesn't.
Our eslint-recommended config is intended for use in addition to the recommended set. The name comes from the fact that it's disabling rules from eslint, i.e. not from the plugin.

It's intended to be a list of rules which don't need to be implemented when you're using typescript, because the typescript compiler already reports errors for them. Really the only time you'd want those rules enabled is if you are using babel, which doesn't do type checking.


you want to exert some influence over the stylistic conventions adopted by the typescript community

We do, and these stylistic conventions haven't just come out of nowhere. The recommended set in v1 was based on combining 3 community maintained configs together, whilst also taking into consideration what the microsoft does in their codebase, as well as what standards are used in the official typescript documentation.

We know that eslint's recommended set purposely doesn't have any opinionated stylistic rules. Their intention is to just onboard people with a small set of rules which help catch problems at lint time, because vanilla JS has no checks otherwise.

We have chosen a slightly different brand for the recommended set, as I mentioned:

Our stance with the recommended set is to give people the best practices by default. This includes rules which are considered "stylistic" rules.

There is no documented guidelines that I can find which explicitly state that recommended sets should only do X. It's up to the plugin author as to what they recommend.

@brainkim
Copy link
Contributor

brainkim commented Aug 2, 2019

Are you using @typescript-eslint/eslint-recommended and eslint-recommended interchangeably, or when you say eslint-recommended, are you referring to eslint's recommended set?

My bad. My usage in the comment above:

  • @typescript-eslint/eslint-recommended - the config provided in this repository
  • eslint-recommended - the config provided by eslint

I do not discuss @typescript-eslint/eslint-recommended except in reference to a possible looser set of rules. The fact that there can be confusion even in this thread is a point in favor of not changing the semantic meaning of “recommended.”

I think you've misunderstood the intention behind our eslint-recommended config.

Again, more evidence in favor of the idea that calling opinionated rules “recommended” is surprising. The distinction between @typescript-eslint/eslint-recommended and @typescript-eslint/recommended is subtle and not one that I think many people care about.

There is no documented guidelines that I can find which explicitly state that recommended sets should only do X. It's up to the plugin author as to what they recommend.

This is a fair point, but I still think calling “best practices” “recommended” is confusing, given its de facto usage by eslint. I would consider calling the ruleset @typescript-eslint/best-practices.

In my opinion, inclusion into a config called recommended should meet the following conditions:

  1. The rule catches bugs
  2. The rule forbids code which is written unintentionally >95% of the time (i.e. there is no reason, performance, environment-specific or otherwise to use the forbidden code)
  3. The rule either does not have an analogous eslint rule, or that analogous eslint rule is included in eslint-recommended

This might sound strict, but I believe setting a high standard is good insofar as it reduces configuration load for people starting new projects. This excludes almost every rule which is describe above as being “a best practice,” but I don’t think this is a bad thing insofar as it provides a solid base to build upon which doesn’t mislead users.

At the same time, typescript provides new opportunities for rules that catch more bugs because we have type information. Some examples of rules which I think merit inclusion based on the above conditions:

  • @typescript-eslint/no-for-in-array
  • @typescript-eslint/restrict-plus-operands

@bradzacher
Copy link
Member

Again, more evidence in favor of the idea that calling opinionated rules “recommended” is surprising. The distinction between @typescript-eslint/eslint-recommended and @typescript-eslint/recommended is subtle and not one that I think many people care about.

I mean, it's not subtle at all, and we have documentation which clearly documents their usage and their intention:
https://github.com/typescript-eslint/typescript-eslint/tree/master/packages/eslint-plugin/src/configs#eslint-recommended

In addition eslint-recommended has a comment which also explains what it does: https://github.com/typescript-eslint/typescript-eslint/blob/master/packages/eslint-plugin/src/configs/eslint-recommended.ts#L1-L4

The rule either does not have an analogous eslint rule, or that analogous eslint rule is included in eslint-recommended

So for example we have our version of require-await turned on, because with type information it handles the "async function with no await but it returns a promise" case that the base rule can't handle.

You're saying that we shouldn't turn that on, even though it'll prevent you accidentally writing code which generates a bunch of literally useless runtime code, because eslint base doesn't recommend it baed on the fact that it doesn't handle the case we can?


but I still think calling “best practices” “recommended” is confusing
mislead users

I fail to understand how we're misleading users.

What is the definition of recommended? "advised or suggested as good or suitable".
We are suggesting that you follow a set of best practices including a few stylistic decisions around consistent naming and delimiters.

The "convention" of only providing a minor subset of rules is only strictly followed by eslint, but it isn't documented anywhere outside of tribal knowledge. And no doubt, they do this purely because people are so vehemently opinionated about styling that the issues would create noise from a userbase of ~7mil weekly downloads.

The typescript community is a lot more standardised. We know this because we've seen it - people have followed the lead of companies like Microsoft (from their typescript repos and documentation) and Google (from their angular2+ project and documentation). We are telling people that these stylistic standards are a good thing to use because there are industry leaders using them.

Ultimately there's no requirement to use the recommended set, nor is there a requirement to provide zero config within your local config if you don't agree with our suggestions.

@brainkim
Copy link
Contributor

brainkim commented Aug 3, 2019

I fail to understand how we're misleading users.

Let me provide a succinct argument for why calling a more opinionated ruleset @typescript-eslint/recommended is misleading.

  1. The typescript-eslint repository provides rules which “shadow” rules provided by eslint via naming conventions. For instance, @typescript-eslint/no-unused-vars shadows eslint’s no-unused-vars, and exists because the eslint rule produces false positives when applied to a TypeScript-flavored AST.
  2. Given that typescript-eslint provides rules which shadow eslint’s rules, a developer can reasonably assume that typescript-eslint’s configs similarly shadow eslint’s configs.
  3. Adding rules which shadow an eslint rule but are not included in eslint-recommended violates the above expectation of config shadowing and is therefore “misleading.”

This argument has nothing to do with the actual English definition of “recommended” and everything to do with conventions established both by eslint and this repository itself. As programmers, anytime we can intuit or establish naming conventions, we have a prima facie obligation to do so insofar as this adheres to the “principle of least astonishment.”

This argument also has litle to do with the way other eslint plugins apply the word “recommended.” Sure, other eslint libraries apply more opinionated rules in their ”recommended” ruleset and have on occasion received a lot of flak for doing so (I’m looking at you eslint-plugin-react/prop-types). However, I argue that this library should be held to a higher standard because it is unique in that it has rules which shadow eslint rules.

Now let me describe a typical user journey given the current config names:

  1. Developer wants to adopt typescript-eslint in an existing eslint repository which is migrating to typescript.
  2. Developer adds "parser": "@typescript-eslint/parser" and "plugins": ["@typescript-eslint"] to their .eslintrc.json.
  3. Developer runs eslint, which explodes with false positives (no-unused-vars).
  4. Developer adds "plugin:@typescript-eslint/recommended" to the extends array which already contains `"eslint:recommended" in attempt to squash these errors.
  5. Developer runs eslint, which explodes with new stylistic errors (indent, unexpected-any).
  6. Assuming that they misunderstood the documentation, developer goes back and discovers @typescript-eslint/eslint-recommended. Developer removes "plugin:@typescript-eslint/recommended and adds "plugin:@typescript-eslint/eslint-recommended" to the array.
  7. Developer runs eslint, only to again have it explode with false positive errors.
  8. Developer goes back to "@typescript-eslint/recommended`, and begins the process of disabling the stylistic rules it provides one by one.

Friends. I have a confession. I am the developer described above. I also believe this is probably a distressingly common user journey. Ultimately, what I was looking for was a config which stopped the false positives caused by "eslint:recommended". What I found was two configs named “eslint-recommended” which either did too much or too little.

So for example we have our version of require-await turned on, because with type information it handles the "async function with no await but it returns a promise" case that the base rule can't handle.

Let’s run @typescript-eslint/require-await through the framework I describe above for a rule which merits inclusion in a “recommended” ruleset.

  1. Does the rule catch bugs?

Not really. Even if a developer accidentally refactors an async function to a sync function, the typescript compiler is already really good at telling the developer when they use a function’s return value in an unexpected way. I don’t think this rule catches bugs which aren’t already caught by the type checker.

  1. Does the rule only forbid code which is written unintentionally?

No. There are many reasons why a developer would write an async function which does not contain await expression. For instance:

  • The developer wants to make sure that any sync errors thrown within the function cause a promise rejection rather than a synchronous error.
  • The developer does not want to return Promise.resolve or Promise.reject while still wanting their function to return a promise.
  1. Does the rule either not have an analogous eslint rule, or is that analogous eslint rule included in eslint-recommended?

No. You’re saying that this rule could be enabled because it catches additional cases thanks to typescript-eslint’s typechecking abilities, but I don’t think handling “an async function with no await which returns a promise” somehow prevents this rule from catching the cases described in test 2. Indeed, I was looking at why this rule even exists, and it’s because there’s another typescript-eslint rule (promise-function-async) which forces you to prefix functions which return promises with async. In my view, I feel that these rules should be mutually exclusive i.e. you either believe that all promise returning functions should be async or that all async functions should always have an await expression, not both, but whatever.

In any case, the fact that you’re adding this rule to recommended means you’re relitigating things that have been rigorously scrutinized by the eslint maintainers themselves (eslint/eslint#8865, eslint/eslint#6820 (comment)). If, as you say, eslint has a “userbase of ~7mil downloads” who are “vehemently opinionated”, isn’t contravening the battle-tested eslint ruleset an act of hubris?

Let’s not get lost in the weeds about specific rules. Ultimately my objections aren’t about any specific rule and more about naming, which is an important part of our jobs as programmers. There is no reason why you couldn’t name this more opinionated ruleset @typescript-eslint/microsoft or @typescript-eslint/best-practices insofar as config extensions are opt-in anyways, and I’m sure it would be a popular ruleset. I also think @typescript-eslint/eslint-recommended could be renamed to @typescript-eslint/eslint-recommended-disable-redundant or something similar to clarify the ruleset’s intent. But I believe there is a clear and present need for a more basic typescript-eslint ruleset which squashes the false positives caused by eslint-recommended and adheres to its spirit and intent, and the most natural names for this ruleset is currently being squatted on by two rulesets which do no such thing.

@bradzacher
I probably won’t argue this further or at the very least will space out my replies across several days because I believe we’re escalating the tone of our rhetoric (e.g. “Ultimately there's no requirement to use the recommended set, nor is there a requirement to provide zero config within your local config if you don't agree with our suggestions.”) Again, thank you for working on this library and I understand you are well within your right to name things whatever you want within this repository. However, if you’re not willing to concede that naming your opinionated ruleset “recommended” could be misleading to some users, I don’t think you’re arguing in good faith. I would also appreciate if another maintainer like @JamesHenry could chime in with their thoughts.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
breaking change This change will require a new major version to be released has pr there is a PR raised to close this package: eslint-plugin Issues related to @typescript-eslint/eslint-plugin recommended-rules Discussion about recommended rule sets
Projects
None yet
Development

Successfully merging a pull request may close this issue.

6 participants