SPFx v1.15+: Consider dialing back ESLint rules #8290
Replies: 6 comments 11 replies
-
How's this for a solution... Microsoft can use the settings they like... but developers can override the settings with a set of their preferred settings. If they do nothing, they get MSFT's rules as they are in 1.15. But if they create the file in the well-known path, they use their settings. TL;DRProposed solutionIn SPFx v1.15.1+, change the .eslintrc.js file to be the following require('@rushstack/eslint-config/patch/modern-module-resolution');
const fs = require('fs');
const path = require('path');
const myRules = path.join(require('os').homedir(),'.spfx-eslint-rules.js');
const baseEslintConfig = {
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
parserOptions: { tsconfigRootDir: __dirname }
};
const esLintConfig = (fs.existsSync(myRules))
// user-defined rules
? { ...baseEslintConfig, rules: require(myRules) }
// Microsoft defined rules
: { ...baseEslintConfig };
module.exports = esLintConfig; As is, this will work in existing SPFx v1.15 projects. But add the following file JSON to the file ~/.spfx-eslint-rules.js: module.exports = {
"@microsoft/spfx/no-async-await": "off",
"react/jsx-no-bind": "off"
} Now, when I run this in a SPFx v1.15 project where I'm using the ... and if the dev uses ESLint in other places in addition to SPFx projects, if you want to use those, you can always pull them in and add to them, modify them, or whatever. So their ~/.spfx-eslint-rules.js would look like: const path = require('path');
module.exports = require(path.join(require('os').homedir(),'.eslint-rules.js')); |
Beta Was this translation helpful? Give feedback.
-
Thank you for the feedback! |
Beta Was this translation helpful? Give feedback.
-
If it can help, a simpler way to tweaks rules is to override rules for TS files : .eslintrc.js : require('@rushstack/eslint-config/patch/modern-module-resolution');
module.exports = {
extends: ['@microsoft/eslint-config-spfx/lib/profiles/react'],
parserOptions: { tsconfigRootDir: __dirname },
ignorePatterns: ['**/node_modules', '**/*.js', '**/*.json', '**/*.scss'],
overrides: [
{
files: ['*.ts', '*.tsx'], // Your TypeScript files extension
parserOptions: {
project: ['./tsconfig.json'], // Specify it only for TypeScript files
},
rules: {
'react-hooks/exhaustive-deps': 'error',
'@typescript-eslint/no-unused-vars': 'warn',
'react/prop-types': 'off',
'react/display-name': 'off',
'no-useless-escape': 'off',
'@microsoft/spfx/no-async-await': 'off',
'@typescript-eslint/typedef': 'off',
'@typescript-eslint/no-floating-promises': 'warn',
},
},
],
}; |
Beta Was this translation helpful? Give feedback.
-
I am for @andrewconnell proposal |
Beta Was this translation helpful? Give feedback.
-
Hello, ESLint is a great improvement for SPFx, but it will take me a lot of time to tweak the configuration and get the same results we had for TSLint. Don't want to describe all rules, here is one rule I do not fully understand:
Thanks |
Beta Was this translation helpful? Give feedback.
-
Thank you @andrewconnell - I agree. Default rules should be dialed down. 👍 |
Beta Was this translation helpful? Give feedback.
-
In the latest release of the SharePoint Framework (SPFx), v1.15, Microsoft ditched TSLint in favor of ESLint. While this is a big improvement and a step in the right direction, I think Microsoft went overboard as they added a ton of additional linting rules we didn’t have to deal with before.
Let me be clear: I like ESLint and I think having rules to either enforce coding standards and/or good practices is a good thing. I like that SPFx projects now use ESLint over the long-deprecated TSLint.
What I don’t like is a vendor telling developers how they should be writing my code, especially a vendor imposing subjective coding styles like how we name variables, or that we use the default member accessors rather than spelling it out.
This is an overreaching position by Microsoft, a disappointing one and that I hope they change in a future SPFx release. Microsoft, or any vendor for that matter, shouldn’t include opinionated linting rules in default projects that could impact a developer, or a development team’s, coding style. That’s not their role; that’s up to the developer, dev team, or customer. Otherwise, Microsoft sounds a lot like Steve Jobs telling me I’m holding my phone wrong.
Some of the rules Microsoft is imposing on SPFx v1.15 projects are irrelevant and obsolete. For a group that has a history of not keeping their platform current and using outdated tools (Node, React, webpack, TSLint, etc), we shouldn’t be looking for coding style & guidance from the SharePoint team.
How default SPFx projects should look
Certain rules they’ve included make a lot of sense andare welcomed. Rules like floating promises, or no-unused-variables are good and promote good coding practices without venturing into much of an argument.
But other rules, like naming-convention, or explicit-member-accessibility, or no-parameter-properties… those are opinionated and up for debate. In those cases, I don’t think a vendor should be imposing those rules on development teams. Rather, that’s the responsibility & prerogative of the developers or companies to set their own standards.
Yes, ESLint provides ways to disable rules (I cover them in a longer article & video), but default projects shouldn’t require teams to make changes before they start being productive with their solution.
ESLint rule change requests on default SPFx projects
I’m going to enumerate some rules that I propose should be removed / disabled from a default SPFx project.
@typescript-eslint/no-explicit-any
The the rule no-explicit-any rule says you can’t use the
any
type on any of your objects. For instance, what if you don’t know the type? That’s one of the benefits of JavaScript, and thus TypeScript. It supports stuff like this:To satisfy the rule, create an interface that effectively said that object can have any named property that’s an object:
Did that fix the the ESLint issue message? Sure… the code is harder to read. This rule should be up to the developer.
👉 Please disable @typescript-eslint/no-explicit-any.
@typescript-eslint/consistent-type-assertions
This consistent-type-assertions rule enforces consistent usage of type assertions. For example, you shouldn’t mix assertion styles like these two options:
This is an opinion… there’s no problem with both lines above. SPFx projects shouldn’t dictate this style.
👉 Please disable @typescript-eslint/consistent-type-assertions.
@typescript-eslint/explicit-member-accessibility
The explicit-member-accessibility rule says you must explicitly specify accessibility modifiers on class methods and properties. For example, the following code will include a message for this rule:
The problem with the above code is that its missing the
public
prefix on the constructor. In TypeScript, if you don’t use the accessor,public
is assumed. That’s generally known it’s excessive to put it there.While this one debatable, but seems like an unnecessary subjective opinion that is being imposed on my coding preferences.
👉 Please disable @typescript-eslint/explicit-member-accessibility.
@typescript-eslint/no-parameter-properties
The rule no-parameter-properties “disallows the use of parameter properties in class constructors… Parameter properties can be confusing to those new to TypeScript as they are less explicit than other ways of declaring and initializing class members.”
TypeScript includes a shorthand syntax that the code above is taking advantage of. What it does is create a private member on the class and sets its value to the value of the parameter passed in. So, the above code can be written as follows to eliminate this ESLint rule message:
The original code is succinct and easy to comprehend, not to mention it’s using a language feature. The rewritten code takes more time to read and comprehend. Again, another subjective opinion that I don’t think a vendor should be imposing on a developer or a development team who may have their own coding standards.
👉 Please disable @typescript-eslint/no-parameter-properties.
@typescript-eslint/typedef
The other rule the above constructor violates is the typedef rule. This says we must use type annotations anywhere a variable is defined.
In this case, the argument passed in should include the
: string
type annotation in it’s definition:This isn’t necessary… it’s clear to the compiler & anyone reading the code that the type of
someStringArg
is a string because the default value it’s set to is a string. Why add extra code you have to comprehend? The goal isn’t to make JavaScript/TypeScript more like C++, the goal is to encourage good practices.👉 Please remove @typescript-eslint/typedef.
@rushstack/eslint-plugin/no-async-await
Plainly stated, this rule is obsolete. It states that you shouldn’t use the
async
keyword because it can have performance issues in older browsers. So… this code will raise this issue:Uh… we’re transpiring TypeScript to ES5 (see tsconfig.json) in your SPFx projects, and
async
isn’t even in ES5, so the generated JavaScript doesn’t even have that keyword in it. Therefore, it will never touch the browser, so this error is a false positive.👉 Please remove @rushstack/eslint-plugin/no-async-await.
@jsx-eslint/eslint-plugin-react/jsx-no-bind
The react/jsx-no-bind rule states we shouldn’t ever use arrow functions in our React properties. For example, this code breaks this rule:
But here’s the thing… this is related to a performance issue using the
.bind()
method. That perf issue was fixed in Chromium v49… that’s from March 2016!Who disables updates on evergreen browsers? Today most of us are Chromium v103. So not only is this rule obsolete, it’s unnecessary.
Don’t believe me? Then trust what Ryan Florence, the author behind the React Router, said in his blog post: **React, Inline Functions, and Performance.
👉 Please remove @jsx-eslint/eslint-plugin-react/jsx-no-bind.
Point/counterpoint
The above post started from an internal discussion with the SPFx engineering team. A few points from that discussion came up that I want to include here for the benefit of the discussion:
“Believe me or not, rules are good for most devs out there.” & “If we can help people write safe, robust, secure, etc. code by default, we should do that”
Let's take both of these arguments to the logical conclusion: testing is good for all devs & projects out there, so why not go ahead and pack in jest, some sample tests, set rules to force certain code coverage %'s, force me to use Cypress for some UI testing... because tested projects are better than non-tested projects.
If you can help people write safe, robust, secure & testable code by default, you should do that... right?
NO, I don't think so... there's a balance.
At least give developers a way to opt in or opt out of this (you already do this with the minimal template)... esp with the number that are being imposed compared to TSLint.
But nagging build outputs with lining & lack of tests... IMHO, you hurt the adoption & first experience, and confuse people who thing they need to get rid of all these things before publishing their projects, and you impose your opinions on what practices some developers do that are perfectly fine (like using the
private
keyword in my constructor's arguments as a shorthand.“We use TypeScript because it makes it safer and less likely to have issues”
TypeScript was created to make large JavaScript projects more maintainable, straight from the creator's original statements announcing it. One of the benefits of JavaScript is its flexibility... TypeScript allows you to put some structure around it, but you can also go overboard with it, and the default rules that are set are doing just that IMHO.
Beta Was this translation helpful? Give feedback.
All reactions