Best ESLint Configuration for TypeScript and ESM
ESLint is a handy open-source tool that finds and fixes problems in your JavaScript and TypeScript code. Who doesn't want a tiresome mentor suggesting best practices constantly? You can adjust its suggestion according to your preferences using the config file.
In this guide, we will
- Decide between new and old configuration files,
- Decide configuration file type,
- Add TypeScript support,
- Decide and use a style guide,
- Use the TypeScript version of the style guide,
- Extend the style guide with our own rules.
- BONUS: My favorite config
ESLint Configuration
Although you can configure ESLint with comments, using a configuration file would be much better.
The Old vs. New Configuration File
ESLint version 8 introduced a new config file format (eslint.config.js
), but it is not enabled by default and, therefore, not widely supported. The next major version (ver. 9) will make the new format default.
We will stick to the current version's old config format (.eslintrc
) to avoid glitches.
Config File Extension: "js", "cjs", "json," or "yml"?
TLDR; Use .eslint.cjs
and write a programmatic configuration:
module.exports = {
// Your config here.
}
ESLint supports the following configuration files:
- .eslintrc.js
- .eslintrc.cjs
- .eslintrc.yaml
- .eslintrc.yml
- .eslintrc.json
- package.json
json
and yaml
configurations are more straightforward, but I suggest js
or cjs
because you can write JavaScript code and develop advanced structures using JavaScript comments, conditionals, imports, etc.
The old ESLint configuration file does not support ESM syntax inside the configuration file. If our TypeScript module is an ESM module, we must use the cjs
extension to indicate that the config is a CommonJS module.
TypeScript Support
ESLint does not know how to parse TypeScript files out of the box. We will install and add the TypeScript parser to the config.
$ npm install --save-dev @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint typescript
.eslintrc.cjs
module.exports = {
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended'],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
root: true,
};
Style Guides
The best configuration is the configuration that matches your needs. However, starting from scratch and getting a perfect setup is very hard. Thanks to the community, there are tested and proven configurations used by lots of developers. These are called style guides.
Some popular style guides are:
You better start with a style guide and modify the rules when needed according to your coding style.
I suggest Airbnb JavaScript Style Guide because there is a slightly modified TypeScript version is available: eslint-config-airbnb-typescript
Using eslint-config-airbnb-typescript, your final configuration should be like the below:
.eslintrc.cjs
module.exports = {
extends: [
'airbnb',
'airbnb-typescript',
'airbnb/hooks',
'plugin:@typescript-eslint/recommended-type-checked', // @typescript-eslint @v6
'plugin:@typescript-eslint/stylistic-type-checked', // @typescript-eslint @v6
// 'plugin:@typescript-eslint/recommended', // @typescript-eslint @v5
// 'plugin:@typescript-eslint/recommended-requiring-type-checking', // @typescript-eslint @v5
],
};
Add Your Rules
What if you don't like something in the preset style guides? You can add your own rules with rules
property. For example, ESLint reports console statements as errors, but I would like to allow console
statements except console.log()
.
{
rules: {
"no-console": ["error", { allow: ["info", "warn", "error"] }],
}
}
BONUS: What is Best for Me?
My favorite rules/configs are:
- airbnb-base (My favorite style guide)
- airbnb-typescript/base (Airbnb for TypeScript)
- [eslint:recommended] (Builtin ESLint recommended rules)
- plugin:promise/recommended (Enforce best practices for JavaScript promises)
- plugin:unicorn/recommended (Well thought additional rules)
- [prettier] (Turns off all ESLint rules conflicting with prettier)
Final Configuration
Here is a sample configuration with my favorite configs:
.eslintrc.cjs
module.exports = {
parser: "@typescript-eslint/parser",
parserOptions: { project: true },
extends: [
"airbnb-base",
"airbnb-typescript/base", // TURN ON airbnb-base rules.
"eslint:recommended", // TURN ON ESLint recommended rules.
"plugin:@typescript-eslint/strict-type-checked",
"plugin:@typescript-eslint/stylistic-type-checked"
"plugin:promise/recommended",
"plugin:unicorn/recommended",
"prettier", // TURNS OFF all ESLint rules conflicting with prettier.
],
rules: {
"no-prototype-builtins": "off", // Too restrictive, writing ugly code to defend against a very unlikely scenario: https://eslint.org/docs/rules/no-prototype-builtins
"no-useless-constructor": "off",
"no-use-before-define": ["error", { functions: false, classes: true, variables: true }],
"@typescript-eslint/no-use-before-define": ["error", { functions: false, classes: true, variables: true, typedefs: true }],
"@typescript-eslint/no-useless-constructor": "error",
"@typescript-eslint/non-nullable-type-assertion-style": "off",
"lines-between-class-members": ["error", "always", { exceptAfterSingleLine: true }],
"import/prefer-default-export": "off",
"unicorn/prevent-abbreviations": "off", // Common abbreviations are known and readable
"unicorn/no-array-for-each": "off", // Airbnb prefers forEach
"import/no-extraneous-dependencies": "off",
"no-console": ["error", { allow: ["info", "warn", "error"] }],
// TypeScript handles rules below as builtin.
"import/named": "off",
"import/namespace": "off",
"import/default": "off",
"import/no-named-as-default-member": "off",
},
}
Conclusion
I published the config above to the npm. You can try the above configuration using eslint-config-ozum. You can find docs about how to use it on the linked page.
What is your favorite style guide and configs? Share your favorites in the comments to help other developers
Comments ()