FAQs
I am using a rule from ESLint core, and it doesn't work correctly with TypeScript code
This happens because TypeScript adds new features that ESLint doesn't know about.
The first step is to check our list of "extension" rules here.
An extension rule is a rule which extends the base ESLint rules to support TypeScript syntax.
If you find it in there, give it a go to see if it works for you.
You can configure it by disabling the base rule, and turning on the extension rule.
Here's an example with the semi
rule:
{
"rules": {
"semi": "off",
"@typescript-eslint/semi": "error"
}
}
If you don't find an existing extension rule, or the extension rule doesn't work for your case, then you can go ahead and check our issues. The contributing guide outlines the best way to raise an issue.
We release a new version our tooling every week. Please ensure that you check our the latest list of "extension" rules before filing an issue.
I get errors telling me "Having many files run with the default project is known to cause performance issues and slow down linting."
These errors are caused by using the EXPERIMENTAL_useProjectService
allowDefaultProjectForFiles
with an excessively wide glob.
allowDefaultProjectForFiles
causes a new TypeScript "program" to be built for each "out of project" file it includes, which incurs a performance overhead for each file.
To resolve this error, narrow the glob(s) used for allowDefaultProjectForFiles
to include fewer files.
For example:
parserOptions: {
EXPERIMENTAL_useProjectService: {
allowDefaultProjectForFiles: [
- "**/*.js",
+ "./*.js"
]
}
}
You may also need to include more files in your tsconfig.json
.
For example:
"include": [
"src",
+ "*.js"
]
If you cannot do this, please file an issue on typescript-eslint's typescript-estree package telling us your use case and why you need more out-of-project files linted. Be sure to include a minimal reproduction we can work with to understand your use case!
I get errors telling me "ESLint was configured to run ... However, that TSConfig does not / none of those TSConfigs include this file"
These errors are caused by an ESLint config requesting type information be generated for a file that isn't included in the TypeScript configuration.
Fixing the Error
- If you do not want to lint the file:
- Use one of the options ESLint offers to ignore files, namely a
.eslintignore
file, orignorePatterns
config.
- Use one of the options ESLint offers to ignore files, namely a
- If you do want to lint the file:
-
If you do not want to lint the file with type-aware linting:
- Use ESLint's
overrides
configuration with ourdisable-type-checked
config to disable type checking for just that type of file.
- Flat Config
- Legacy Config
eslint.config.jsimport tseslint from 'typescript-eslint';
export default tseslint.config(
// ... the rest of your config ...
{
files: ['**/*.js'],
extends: [tseslint.configs.disableTypeChecked],
},
);.eslintrc.cjsmodule.exports = {
// ... the rest of your config ...
overrides: [
{
extends: ['plugin:@typescript-eslint/disable-type-checked'],
files: ['./**/*.js'],
},
],
};- Alternatively to disable type checking for files manually, you can set
parserOptions: { project: false }
to an override for the files you wish to exclude.
- Use ESLint's
-
If you do want to lint the file with type-aware linting:
- Check the
include
option of each of the TSConfigs that you provide toparserOptions.project
- you must ensure that all files match aninclude
glob, or else our tooling will not be able to find it.- If the file is a
.cjs
,.js
, or.mjs
file, make sureallowJs
is enabled.
- If the file is a
- If your file shouldn't be a part of one of your existing tsconfigs (for example, it is a script/tool local to the repo), then consider creating a new tsconfig (we advise calling it
tsconfig.eslint.json
) in your project root which lists this file in itsinclude
. For an example of this, you can check out the configuration we previously used in this repo:
- Check the
-
More Details
This error may appear from the combination of two things:
- The ESLint configuration for the source file specifies at least one TSConfig file in
parserOptions.project
- None of those TSConfig files includes the source file being linted
- Note that files with the same name and different extension may not be recognized by TypeScript: see
parserOptions.project
docs
- Note that files with the same name and different extension may not be recognized by TypeScript: see
When TSConfig files are specified for parsing a source file, @typescript-eslint/parser
will use the first TSConfig that is able to include that source file (per aka.ms/tsconfig#include) to generate type information.
However, if no specified TSConfig includes the source file, the parser won't be able to generate type information.
This error most commonly happens on config files or similar that are not included in their project TSConfig(s). For example, many projects have files like:
- An
.eslintrc.cjs
/eslint.config.js
withparserOptions.project: ["./tsconfig.json"]
- A
tsconfig.json
withinclude: ["src"]
In that case, viewing the file in an IDE with the ESLint extension will show the error notice that the file couldn't be linted because it isn't included in tsconfig.json
.
See our docs on type aware linting for more information.
I get errors telling me "The file must be included in at least one of the projects provided"
You're using an outdated version of @typescript-eslint/parser
.
Update to the latest version to see a more informative version of this error message, explained above.
How do I turn on a @typescript-eslint
rule?
First make sure you've read the docs and understand ESLint configuration files:
- Read our getting started guide to ensure your config is properly setup to start configuring our rules.
- Checkout ESLint's documentation on configuring rules to ensure you understand how to configure rules.
Our rule docs detail the options each rule supports under the "Options" heading.
We use TypeScript types to describe an Options
tuple type for the rule which you can use to configure the a rule.
In your config file the keys of the rules
object are the names of the rules you wish to configure and the values follow the following form:
type Severity = 'off' | 'warn' | 'error';
type RuleConfig =
| Severity
| [Severity]
| [
Severity,
// Options is the tuple type from the rule docs
...Options,
];
Some examples
- Flat Config
- Legacy Config
export default tseslint.config(
// ... the rest of your config ...
{
rules: {
// turns a rule on with no configuration (i.e. uses the default configuration)
'@typescript-eslint/array-type': 'error',
// turns on a rule with configuration
'@typescript-eslint/no-explicit-any': ['warn', { ignoreRestArgs: true }],
},
},
);
module.exports = {
// ... the rest of your config ...
rules: {
// turns a rule on with no configuration (i.e. uses the default configuration)
'@typescript-eslint/array-type': 'error',
// turns on a rule with configuration
'@typescript-eslint/no-explicit-any': ['warn', { ignoreRestArgs: true }],
},
};
typescript-eslint thinks my variable is never nullish / is any
/ etc., but that is clearly not the case to me
Our type-aware rules almost always trust the type information provided by the TypeScript compiler. Therefore, an easy way to check if our rule is behaving correctly is to inspect the type of the variable in question, such as by hovering over it in your IDE.
If the IDE also shows that the type is never nullish / is any
, you need to fix the type. A very common case is with the no-unnecessary-condition
rule. Take this code for example:
let condition = false;
const f = () => (condition = true);
f();
if (condition) {
//^^^^^^^^^ Unnecessary conditional, value is always falsy.
}
You can see that the type of condition
is actually the literal type false
by hovering over it in your IDE. In this case, typescript-eslint cannot possible know better than TypeScript itself, so you need to fix the report by fixing the type, such as through an assertion (let condition = false as boolean
).
If the IDE provides different type information from typescript-eslint's report, then make sure that the TypeScript setup used for your IDE, typescript-eslint, and tsc
are the same: the same TypeScript version, the same type-checking compiler options, and the same files being included in the project. For example, if a type is declared in another file but that file is not included, the type will become any
, and cause our no-unsafe-*
rules to report.
Can I use ESLint's --cache
with typescript-eslint?
ESLint's --cache
option caches on a per-file basis.
You can use it, but it will only work reliably for untyped rules -- and even then, not always.
Any ESLint rule that checks logic across files, including many rules from eslint-plugin-import
, creates cross-file dependencies.
Typed lint rules almost always have dependencies on types across files in practice.
ESLint's caching doesn't account for those cross-file dependencies.
We don't recommend using --cache
.
I use a framework (like Vue) that requires custom file extensions, and I get errors like "You should add parserOptions.extraFileExtensions
to your config"
You can use parserOptions.extraFileExtensions
to specify an array of non-TypeScript extensions to allow, for example:
- Flat Config
- Legacy Config
export default tseslint.config(
// ... the rest of your config ...
{
languageOptions: {
parserOptions: {
tsconfigRootDir: import.meta.dirname,
project: ['./tsconfig.json'],
extraFileExtensions: ['.vue'],
},
},
},
);
module.exports = {
// ... the rest of your config ...
parserOptions: {
tsconfigRootDir: __dirname,
project: ['./tsconfig.json'],
extraFileExtensions: ['.vue'],
},
};
I am running into errors when parsing TypeScript in my .vue files
If you are running into issues parsing .vue files, it might be because parsers like vue-eslint-parser
are required to parse .vue
files. In this case you can move @typescript-eslint/parser
inside parserOptions
and use vue-eslint-parser
as the top level parser.
- Flat Config
- Legacy Config
import tseslint from 'typescript-eslint';
import vueParser from 'vue-eslint-parser';
export default tseslint.config(
// ... the rest of your config ...
{
languageOptions: {
parser: tseslint.parser,
parser: vueParser,
parserOptions: {
parser: tseslint.parser,
sourceType: 'module',
},
},
},
);
module.exports = {
// ... the rest of your config ...
parser: '@typescript-eslint/parser',
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
extraFileExtensions: ['.vue'],
},
};
The parserOptions.parser
option can also specify an object to specify multiple parsers. See the vue-eslint-parser
usage guide for more details.
One of my lint rules isn't working correctly on a pure JavaScript file
This is to be expected - ESLint rules do not check file extensions on purpose, as it causes issues in environments that use non-standard extensions (for example, a .vue
and a .md
file can both contain TypeScript code to be linted).
If you have some pure JavaScript code that you do not want to apply certain lint rules to, then you can use ESLint's overrides
configuration to turn off certain rules, or even change the parser based on glob patterns.
Should I run ESLint on transpiled output JavaScript files?
No.
Source TypeScript files have all the content of output JavaScript files, plus type annotations. There's no benefit to also linting output JavaScript files.
TypeScript should be installed locally
Make sure that you have installed TypeScript locally i.e. by using npm install typescript
, not npm install -g typescript
,
or by using yarn add typescript
, not yarn global add typescript
.
See #2041 for more information.
How can I ban <specific language feature>
?
ESLint core contains the rule no-restricted-syntax
.
This generic rule allows you to specify a selector for the code you want to ban, along with a custom error message.
You can use an AST visualization tool such as typescript-eslint playground > Options > AST Explorer on its left sidebar by selecting ESTree to help in figuring out the structure of the AST that you want to ban.
For example, you can ban enums (or some variation of) using one of the following configs:
{
"rules": {
"no-restricted-syntax": [
"error",
// ban all enums
{
"selector": "TSEnumDeclaration",
"message": "My reason for not using any enums at all",
},
// ban just const enums
{
"selector": "TSEnumDeclaration[const=true]",
"message": "My reason for not using const enums",
},
// ban just non-const enums
{
"selector": "TSEnumDeclaration:not([const=true])",
"message": "My reason for not using non-const enums",
},
],
},
}
Why don't I see TypeScript errors in my ESLint output?
TypeScript's compiler (or whatever your build chain may be) is specifically designed and built to validate the correctness of your codebase. Our tooling does not reproduce the errors that TypeScript provides, because doing so would slow down the lint run [1], and duplicate the errors that TypeScript already outputs for you.
Instead, our tooling exists to augment TypeScript's built in checks with lint rules that consume the type information in new ways beyond just verifying the runtime correctness of your code.
[1] - TypeScript computes type information lazily, so us asking for the errors it would produce from the compiler would take an additional ~100ms per file. This doesn't sound like a lot, but depending on the size of your codebase, it can easily add up to between several seconds to several minutes to a lint run.
I get errors from the no-undef
rule about global variables not being defined, even though there are no TypeScript errors
The no-undef
lint rule does not use TypeScript to determine the global variables that exist - instead, it relies upon ESLint's configuration.
We strongly recommend that you do not use the no-undef
lint rule on TypeScript projects.
The checks it provides are already provided by TypeScript without the need for configuration - TypeScript just does this significantly better.
As of our v4.0.0 release, this also applies to types.
If you use global types from a 3rd party package (i.e. anything from an @types
package), then you will have to configure ESLint appropriately to define these global types.
For example; the JSX
namespace from @types/react
is a global 3rd party type that you must define in your ESLint config.
Note, that for a mixed project including JavaScript and TypeScript, the no-undef
rule (like any rule) can be turned off for TypeScript files alone as follows:
- Flat Config
- Legacy Config
import tseslint from 'typescript-eslint';
export default tseslint.config(
// ... the rest of your config ...
{
files: ['**/*.{ts,tsx,mts,cts}'],
rules: {
'no-undef': 'off',
},
},
);
module.exports = {
// ... the rest of your config ...
overrides: [
{
files: ['*.ts', '*.mts', '*.cts', '*.tsx'],
rules: {
'no-undef': 'off',
},
},
],
};
If you choose to leave on the ESLint no-undef
lint rule, you can manually define the set of allowed globals
in your ESLint config, and/or you can use one of the pre-defined environment (env
) configurations.
How do I check to see what versions are installed?
If you have multiple versions of our tooling, it can cause various bugs for you. This is because ESLint may load a different version each run depending on how you run it - leading to inconsistent lint results.
Installing our tooling in the root of your project does not mean that only one version is installed.
One or more of your dependencies may have its own dependency on our tooling, meaning npm
/yarn
will additionally install that version for use by that package.
For example, react-scripts
(part of create-react-app
) has a dependency on our tooling.
You can check what versions are installed in your project using the following commands:
- npm
- Yarn
- pnpm
npm list @typescript-eslint/eslint-plugin @typescript-eslint/parser
yarn list --pattern "@typescript-eslint/eslint-plugin|@typescript-eslint/parser"
pnpm list @typescript-eslint/eslint-plugin @typescript-eslint/parser
If you see more than one version installed, then you will have to either use yarn resolutions to force a single version, or you will have to downgrade your root versions to match the dependency versions.
The best course of action in this case is to wait until your dependency releases a new version with support for our latest versions.
How can I specify a TypeScript version / parserOptions.typescriptLocation
?
You can't, and you don't want to.
You should use the same version of TypeScript for linting as the rest of your project. TypeScript versions often have slight differences in edge cases that can cause contradictory information between typescript-eslint rules and editor information. For example:
@typescript-eslint/strict-boolean-expressions
might be operating with TypeScript version X and think a variable isstring[] | undefined
- TypeScript itself might be on version X+1-beta and think the variable is
string[]
See this issue comment for more details.
Changes to one file are not reflected when linting other files in my IDE
tl;dr: Restart your ESLint server to force an update.
ESLint currently does not have any way of telling parsers such as ours when an arbitrary file is changed on disk. That means if you change file A that is imported by file B, it won't update lint caches for file B -- even if file B's text contents have changed. Sometimes the only solution is to restart your ESLint editor extension altogether.
See this issue comment for more information.
VS Code's ESLint extension provides an ESLint: Restart ESLint Server
action.
I get no-unsafe-*
complaints for cross-file changes
See Changes to one file are not reflected in linting other files in my IDE.
Rules such as no-unsafe-argument
, no-unsafe-assignment
, and no-unsafe-call
are often impacted.
"The '<key>
' property is deprecated on '<type>
' nodes. Use '<key>
' instead." warnings
If you're seeing this warning, it's likely you're using an ESLint plugin (or other tooling) that hasn't been updated for typescript-eslint v6. Make sure you're using the latest versions of each of your ESLint plugins (and other tooling).
If you've using many ESLint plugins, have updated each to their latest version, and you're not sure which one this complaint is coming from, try either or both of:
- Running with
--trace-deprecation
(e.g.npx cross-env NODE_OPTIONS=--trace-deprecation npm run lint
) - Disabling half of them at a time to narrow down which plugin it is
Then make sure each of those plugins has a GitHub issue asking that they release a version supporting typescript-eslint v6.
For developers updating ESLint rules in plugins that still need to support typescript-eslint v5: you may need to ||
fall back to the old property key if the new one doesn't exist:
- node.typeParameters
+ node.typeArguments || node.typeParameters
For more context, see the Some properties named typeParameters instead of typeArguments issue, and the implementing fix: rename typeParameters to typeArguments where needed pull request.
The typescript-eslint v6 release post has more information on typescript-eslint v6.
My linting feels really slow
If you think you're having issues with performance, see our Performance Troubleshooting documentation.
Are TypeScript project references supported?
Yes, but only with EXPERIMENTAL_useProjectService
.
See issue #2094 discussing project references for more details.