eslint-config-isaacscript
Introduction
This is a sharable configuration for ESLint that is intended to be used in TypeScript projects.
The config is environment-agnostic, meaning that it will work in client-side projects (e.g. React), server-side projects (e.g. Node.js), and so on.
Installation
This package is part of the isaacscript-lint
meta-linting package. It is recommended that instead of consuming this package directly, you instead list isaacscript-lint
as a dependency, as that will install both this config and all of the rule plugins that it depends on.
For installation instructions, see the isaacscript-lint
page.
Why Do I Need To Use ESLint?
If you are reading this page, you are likely a user of TypeScript. As you probably know, TypeScript is great because it saves you an enormous amount of time. The hours spent troubleshooting run-time errors caused from small typos have become a thing of the past. Good riddance!
But there are many other code problems that do not have to do with types. In the same way that you want to use TypeScript to catch as many bugs as possible, you also want to use ESLint with a config that enables lots of good linting rules to catch even more bugs.
ESLint rules can help catch bugs, but they can also help to make your codebase more consistent and adhere to best-practices within the TypeScript ecosystem. Remember that code is read more often than it is written. If you care about your code being the best that it can possibly be, then using ESLint is a no-brainer!
Why Do I Need eslint-config-isaacscript
?
Your codebase deserves to be safe as possible and eslint-config-isaacscript
is the most comprehensive ESLint config out there.
Building an ESLint config from scratch takes many, many hours. ESLint has over 250 rules. typescript-eslint
has over 125 rules. And that's just the beginning.
Don't bother creating and maintaining a huge ESLint config yourself. We've done the work to:
- Enable every ESLint rule that we can find from trusted sources that provides value.
- Weed out the rules that don't apply to TypeScript codebases (because many ESLint rules were written before TypeScript existed).
- Weed out the rules covered by Prettier (because many ESLint rules were written before Prettier existed).
- Weed out the rules that are too noisy to provide value (and document them below).
Our Config Philosophy
We want to enable as many lint rules as possible, so that we can catch as many bugs as possible. Of course, this is a tradeoff: with more lint rules, we get more false positives. But in general, a few false positives are worth the time saved from investigating and squashing bugs. (More on false positives later.)
In line with this philosophy, our linting config enables nearly all of the recommended rules from both the core ESLint team and the TypeScript ESLint team, as well as some additional rules that catch even more bugs.
This config also assumes that you are using Prettier to format your TypeScript code, which is considered to be best-practice in the ecosystem. Subsequently, all formatting-related rules that conflict with Prettier are disabled. (However, we use a few formatting-related rules that are not handled by Prettier.)
Auto-Fixing
Deploying this ESLint config on an existing codebase can generate a ton of warnings. Fixing them all might seem overwhelming. While some warnings need to be fixed manually, a ton of ESLint rules have "auto-fixers". This means that the code will fix itself if you run ESLint with the --fix
flag. So, by running npx eslint --fix .
in the root of your project, you can take care of a lot of the warnings automatically.
Additionally, we recommend that you configure your IDE (i.e. VSCode) to automatically run --fix
whenever you save a file.
Dealing with False Positives
Your first reaction to having a bunch of yellow squiggly lines might be to disable any rule that gets in your way. However, even if you think an ESLint warning is superfluous, it is often a sign that your codebase is structured in a bug-prone or non-idiomatic way. Before simply disabling a rule, sometimes it is good to do some research and think carefully if your code can be refactored in some way to be cleaner.
Additionally, some ESLint rules are not about catching bugs, but are about code style and code consistency. If you find the new style to be foreign and weird, it can be tempting to ignore or disable the rule. But before you do that, consider the cost: your codebase will be deviating from others in the TypeScript ecosystem. It is really nice for everyone's code to adhere to the same look and the same standards!
With that said, with so many ESLint rules turned on, you will undoubtedly come across some false positives. You can quickly take care of these by adding a // eslint-disable-next-line insert-rule-name-here
comment. And you can automatically add the comment by selecting "Quick Fix" in VSCode, which is mapped to Ctrl + .
by default.
If you find yourself adding a lot of disable comments for a specific rule, then turn the rule off for the entire project by adding an entry for it in your .eslintrc.cjs
file. Some rules won't make sense for every project and that's okay!
Rule List
Below, we provide documentation for every rule that is disabled. (We take a blacklist approach rather than a whitelist approach.)
Core ESLint Rules
eslint-plugin-no-autofix
Rules
Rule Name | Enabled | Parent Configs | Notes | Source File |
---|---|---|---|---|
no-autofix/no-useless-return | ✅ | It is common during development to comment out code after an early return. In these cases, the auto-fixer is harmful, since it would require us to manually go put the return statement back after uncommenting the code. | base-no-autofix.js | |
no-autofix/prefer-const | ✅ | It is common during development to comment out code that modifies a let variable. In these cases, the auto-fixer is harmful, since it would require us to manually go change the const back to a let after uncommenting the code. | base-no-autofix.js |
@typescript-eslint
Rules
eslint-plugin-eslint-comments
Rules
Rule Name | Enabled | Parent Configs | Notes | Source File |
---|---|---|---|---|
eslint-comments/disable-enable-pair | ✅ | The allowWholeFile option is enabled because it is common practice to use "eslint-disable" comments for a whole file. | base-eslint-comments.js | |
eslint-comments/no-aggregating-enable | ✅ | base-eslint-comments.js | ||
eslint-comments/no-duplicate-disable | ✅ | base-eslint-comments.js | ||
eslint-comments/no-restricted-disable | ❌ | Disabled because it is only useful in projects that want to prevent disabling specific ESLint rules. | base-eslint-comments.js | |
eslint-comments/no-unlimited-disable | ❌ | Disabled because if a line breaks three or more ESLint rules, then it is useful to use a single "eslint-disable" comment to make things more concise. | base-eslint-comments.js | |
eslint-comments/no-unused-disable | ✅ | base-eslint-comments.js | ||
eslint-comments/no-unused-enable | ✅ | base-eslint-comments.js | ||
eslint-comments/no-use | ❌ | Disabled because we want to allow disabling ESLint rules where appropriate. | base-eslint-comments.js | |
eslint-comments/require-description | ❌ | Disabled because requiring descriptions for every ESLint disable would be too cumbersome. | base-eslint-comments.js |
eslint-plugin-import
Rules
eslint-plugin-jsdoc
Rules
Rule Name | Enabled | Parent Configs | Notes | Source File |
---|---|---|---|---|
jsdoc/check-access | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/check-alignment | ❌ | Superseded by the isaacscript/limit-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/check-examples | ❌ | Disabled since it does not work with ESLint 8. | base-jsdoc.js | |
jsdoc/check-indentation | ❌ | Superseded by the isaacscript/limit-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/check-line-alignment | ❌ | Disabled since this is not a common formatting scheme. It is also not recommended by the plugin authors. | base-jsdoc.js | |
jsdoc/check-param-names | ✅ | base-jsdoc.js | ||
jsdoc/check-property-names | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/check-syntax | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/check-tag-names | ✅ | base-jsdoc.js | ||
jsdoc/check-template-names | ✅ | base-jsdoc.js | ||
jsdoc/check-types | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/check-values | ✅ | base-jsdoc.js | ||
jsdoc/convert-to-jsdoc-comments | ❌ | Disabled since it is idiomatic in the TypeScript ecosystem to use a mixture of both JSDoc and non-JSDoc comments. | base-jsdoc.js | |
jsdoc/empty-tags | ✅ | base-jsdoc.js | ||
jsdoc/implements-on-classes | ✅ | base-jsdoc.js | ||
jsdoc/imports-as-dependencies | ❌ | Disabled since you cannot configure it with a path to the correct "package.json" file. | base-jsdoc.js | |
jsdoc/informative-docs | ✅ | base-jsdoc.js | ||
jsdoc/match-description | ❌ | Superseded by the isaacscript/jsdoc-full-sentences rule. | base-jsdoc.js | |
jsdoc/match-name | ❌ | Disabled because it is only needed for projects with specific JSDoc requirements. | base-jsdoc.js | |
jsdoc/multiline-blocks | ❌ | Superseded by the isaacscript/limit-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/newline-after-description | ❌ | Superseded by the isaacscript/limit-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/no-bad-blocks | ❌ | Disabled because it provides little value; it only detects JSDoc comments with tags in them. | base-jsdoc.js | |
jsdoc/no-blank-block-descriptions | ❌ | Superseded by the isaacscript/format-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/no-blank-blocks | ❌ | Superseded by the isaacscript/no-empty-jsdoc rule. | base-jsdoc.js | |
jsdoc/no-defaults | ❌ | Disabled because it provides little value; the @default tag is rare. | base-jsdoc.js | |
jsdoc/no-missing-syntax | ❌ | Disabled because it is too project-specific. | base-jsdoc.js | |
jsdoc/no-multi-asterisks | ❌ | Superseded by the isaacscript/limit-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/no-restricted-syntax | ❌ | Disabled because it is intended for disabling of specific language features per-project. | base-jsdoc.js | |
jsdoc/no-types | ✅ | The contexts option is set to any to make the rule stricter. | base-jsdoc.js | |
jsdoc/no-undefined-types | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/require-asterisk-prefix | ✅ | base-jsdoc.js | ||
jsdoc/require-description | ❌ | Disabled because it is overboard for every function to have a description. | base-jsdoc.js | |
jsdoc/require-description-complete-sentence | ❌ | Superseded by the isaacscript/jsdoc-complete-sentences rule. | base-jsdoc.js | |
jsdoc/require-example | ❌ | Disabled because it is overboard for every function to require an example. | base-jsdoc.js | |
jsdoc/require-file-overview | ❌ | Disabled because it is overboard for every file to require an overview. | base-jsdoc.js | |
jsdoc/require-hyphen-before-param-description | ✅ | The never option is provided to make the rule match the format of the official TypeScript codebase. | base-jsdoc.js | |
jsdoc/require-jsdoc | ❌ | Disabled since it is overboard for every function to have a JSDoc comment. | base-jsdoc.js | |
jsdoc/require-param | ✅ | Configured to only apply when there are one or more parameters. | base-jsdoc.js | |
jsdoc/require-param-description | ✅ | The contexts option is set to any to make the rule stricter. | base-jsdoc.js | |
jsdoc/require-param-name | ✅ | The contexts option is set to any to make the rule stricter. | base-jsdoc.js | |
jsdoc/require-param-type | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/require-property | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/require-property-description | ✅ | base-jsdoc.js | ||
jsdoc/require-property-name | ✅ | base-jsdoc.js | ||
jsdoc/require-property-type | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/require-returns | ❌ | Disabled because it is overboard for every function to document every return value. | base-jsdoc.js | |
jsdoc/require-returns-check | ❌ | Disabled because it is overboard for every function to document every return value. | base-jsdoc.js | |
jsdoc/require-returns-description | ✅ | The contexts option is set to any to make the rule stricter. | base-jsdoc.js | |
jsdoc/require-returns-type | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js | |
jsdoc/require-template | ❌ | Disabled because it is overboard to document every generic type variable. | base-jsdoc.js | |
jsdoc/require-throws | ❌ | Disabled because it is overboard to document every throw statement. | base-jsdoc.js | |
jsdoc/require-yields | ❌ | Disabled because it is overboard to document every yield. | base-jsdoc.js | |
jsdoc/require-yields-check | ❌ | Disabled because it is overboard to document every yield. | base-jsdoc.js | |
jsdoc/sort-tags | ❌ | Disabled because it is not very useful. In most cases, a function will only have @param and @return tags, making sorting unnecessary. | base-jsdoc.js | |
jsdoc/tag-lines | ❌ | Superseded by the isaacscript/format-jsdoc-comments rule. | base-jsdoc.js | |
jsdoc/text-escaping | ❌ | Disabled since it is only useful in certain environments (e.g. when your project converts JSDoc comments to Markdown). | base-jsdoc.js | |
jsdoc/valid-types | ❌ | Disabled because it is not needed in TypeScript. | base-jsdoc.js |
eslint-plugin-n
Rules
Rule Name | Enabled | Parent Configs | Notes | Source File |
---|---|---|---|---|
n/callback-return | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/exports-style | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/file-extension-in-import | ✅ | This rule is helpful to automatically fix file extensions in import statements throughout an entire codebase. | base-n.js | |
n/global-require | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/handle-callback-err | ✅ | base-n.js | ||
n/hashbang | ❌ | Disabled since it does not work very well with TypeScript. (It needs project-specific configuration depending on where the output directory is located.) | base-n.js | |
n/no-callback-literal | ✅ | base-n.js | ||
n/no-deprecated-api | ✅ | base-n.js | ||
n/no-exports-assign | ✅ | base-n.js | ||
n/no-extraneous-import | ❌ | Disabled since it is handled by the TypeScript compiler. | base-n.js | |
n/no-extraneous-require | ❌ | Disabled since require statements are not used in TypeScript code. | base-n.js | |
n/no-hide-core-modules | ❌ | Disabled because this rule is deprecated. | base-n.js | |
n/no-missing-import | ❌ | Disabled since it is handled by the TypeScript compiler. | base-n.js | |
n/no-missing-require | ✅ | base-n.js | ||
n/no-mixed-requires | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/no-new-require | ✅ | base-n.js | ||
n/no-path-concat | ✅ | base-n.js | ||
n/no-process-env | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/no-process-exit | ❌ | Disabled because using process.exit is common to exit command-line applications without verbose output. | base-n.js | |
n/no-restricted-import | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/no-restricted-require | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/no-sync | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/no-unpublished-bin | ✅ | base-n.js | ||
n/no-unpublished-import | ✅ | An exception is made for files in a "scripts" directory, since those should be allowed to import from "devDependencies". | base-n.js | |
n/no-unpublished-require | ✅ | base-n.js | ||
n/no-unsupported-features | ❌ | Disabled because this rule is deprecated. | base-n.js | |
n/no-unsupported-features/es-builtins | ❌ | Disabled because it is assumed that we are running on modern versions of Node.js. | base-n.js | |
n/no-unsupported-features/es-syntax | ❌ | Disabled because it is assumed that our transpiler or runtime has support for the latest version of ESM. | base-n.js | |
n/no-unsupported-features/node-builtins | ❌ | Disabled since it is handled by the TypeScript compiler. | base-n.js | |
n/prefer-global/buffer | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-global/console | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-global/process | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-global/text-decoder | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-global/text-encoder | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-global/url | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-global/url-search-params | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-node-protocol | ❌ | Superseded by the unicorn/prefer-node-protocol rule. | base-n.js | |
n/prefer-promises/dns | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/prefer-promises/fs | ❌ | Disabled since stylistic rules from this plugin are not used. | base-n.js | |
n/process-exit-as-throw | ✅ | base-n.js | ||
n/shebang | ❌ | Superseded by the n/hashbang rule. | base-n.js |