Linting with Type Information
Some typescript-eslint rules utilize the awesome power of TypeScript's type checking APIs to provide much deeper insights into your code. To tap into TypeScript's additional powers, there are two small changes you need to make to your config file:
- Flat Config
- Legacy Config
- Add
TypeChecked
to the name of any preset configs you're using, namelyrecommended
,strict
, andstylistic
. - Add
languageOptions.parserOptions
to tell our parser how to find the TSConfig for each source file.
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
);
import.meta.dirname
is only present for ESM files in Node.js >=20.11.0 / >= 21.2.0.
For CommonJS modules and/or older versions of Node.js, use __dirname
or an alternative.
In more detail:
tseslint.configs.recommendedTypeChecked
is another shared configuration we provide. This one contains recommended rules that additionally require type information.parserOptions.projectService: true
indicates to ask TypeScript's type checking service for each source file's type information (see Parser#projectService).parserOptions.tsconfigRootDir
tells our parser the absolute path of your project's root directory (see Parser#tsconfigRootDir).
- Add
-type-checked
to the name of any preset configs you're using, namelyrecommended
,strict
, andstylistic
. - Add
parserOptions
to tell our parser how to find the TSConfig for each source file.
/* eslint-env node */
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-type-checked',
],
plugins: ['@typescript-eslint'],
parser: '@typescript-eslint/parser',
parserOptions: {
projectService: true,
tsconfigRootDir: __dirname,
},
root: true,
};
In more detail:
plugin:@typescript-eslint/recommended-type-checked
is another shared configuration we provide. This one contains recommended rules that additionally require type information.parserOptions.projectService: true
indicates to ask TypeScript's type checking service for each source file's type information (see Parser#projectService).parserOptions.tsconfigRootDir
tells our parser the absolute path of your project's root directory (see Parser#tsconfigRootDir).
Your ESLint config file may start receiving a parsing error about type information. See our TSConfig inclusion FAQ.
With that done, run the same lint command you ran before. You may see new rules reporting errors based on type information!
Shared Configurations
If you enabled the strict
shared config and/or stylistic
shared config in a previous step, be sure to replace them with strictTypeChecked
and stylisticTypeChecked
respectively to add their type-checked rules.
- Flat Config
- Legacy Config
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.strict,
...tseslint.configs.stylistic,
...tseslint.configs.strictTypeChecked,
...tseslint.configs.stylisticTypeChecked,
// ...
);
/* eslint-env node */
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/strict',
'plugin:@typescript-eslint/stylistic',
'plugin:@typescript-eslint/strict-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
],
// ...
};
You can read more about the rules provided by typescript-eslint in our rules docs and shared configurations docs.
FAQs
Can I customize the TSConfig used for typed linting?
Yes, but it's not recommended in most configurations.
parserOptions.projectService
uses the same "project service" APIs used by editors such as VS Code to generate TypeScript's type information.
Using a different TSConfig runs the risk of providing different types for typed linting than what your editor or tsc
see.
If you absolutely must, the parserOptions.project
option can be used instead of parserOptions.projectService
with either:
true
: to always usetsconfig.json
s nearest to source filesstring | string[]
: any number of glob paths to match TSConfig files relative toparserOptions.tsconfigRootDir
, or the current working directory if that is not provided
For example, if you use a specific tsconfig.eslint.json
for linting, you'd specify:
- Flat Config
- Legacy Config
export default tseslint.config({
// ...
languageOptions: {
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: import.meta.dirname,
},
},
// ...
});
module.exports = {
// ...
parserOptions: {
project: './tsconfig.eslint.json',
tsconfigRootDir: __dirname,
},
// ...
};
See the @typescript-eslint/parser
project
docs for more details.
If your project is a multi-package monorepo, see our docs on configuring a monorepo.
How can I disable type-aware linting for a subset of files?
You can combine ESLint's overrides config in conjunction with our disable-type-checked
config to turn off type-aware linting on specific subsets of files.
- Flat Config
- Legacy Config
export default tseslint.config(
eslint.configs.recommended,
...tseslint.configs.recommendedTypeChecked,
...tseslint.configs.stylisticTypeChecked,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.name,
},
},
},
{
files: ['**/*.js'],
...tseslint.configs.disableTypeChecked,
},
);
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/recommended-type-checked',
'plugin:@typescript-eslint/stylistic-type-checked',
],
plugins: ['@typescript-eslint'],
parser: '@typescript-eslint/parser',
parserOptions: {
projectService: true,
tsconfigRootDir: __dirname,
},
root: true,
overrides: [
{
files: ['*.js'],
extends: ['plugin:@typescript-eslint/disable-type-checked'],
},
],
};
If you use type-aware rules from other plugins, you will need to manually disable these rules or use a premade config they provide to disable them.
How is performance?
Typed rules come with a catch. By using typed linting in your config, you incur the performance penalty of asking TypeScript to do a build of your project before ESLint can do its linting. For small projects this takes a negligible amount of time (a few seconds or less); for large projects, it can take longer.
Most of our users do not mind this cost as the power and safety of type-aware static analysis rules is worth the tradeoff. Additionally, most users primarily consume lint errors via IDE plugins which, through caching, do not suffer the same penalties. This means that generally they usually only run a complete lint before a push, or via their CI, where the extra time often doesn't matter.
We strongly recommend you do use type-aware linting, but the above information is included so that you can make your own, informed decision.
Troubleshooting
If you're having problems getting this working, please have a look at our Troubleshooting FAQs.