General
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 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 }],
},
};
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.
Banning confusing property uses
{
"rules": {
"no-restricted-syntax": [
"error",
// Ban accessing `constructor.name`:
{
"selector": "MemberExpression[object.property.name='constructor'][property.name='name']",
"message": "'constructor.name' is not reliable after code minifier usage.",
},
// Ban get and set accessors:
{
"selector": "Property:matches([kind = \"get\"], [kind = \"set\"]), MethodDefinition:matches([kind = \"get\"], [kind = \"set\"])",
"message": "Don't use get and set accessors.",
},
],
},
}
Banning specific uses of class properties
{
"rules": {
"no-restricted-syntax": [
"error",
// Ban `private` members:
{
"selector": ":matches(PropertyDefinition, MethodDefinition)[accessibility=\"private\"]",
"message": "Use `#private` members instead.",
},
// Ban `#private` members:
{
"selector": ":matches(PropertyDefinition, MethodDefinition) > PrivateIdentifier.key",
"message": "Use the `private` modifier instead.",
},
// Ban static `this`:
{
"selector": "MethodDefinition[static = true] ThisExpression",
"message": "Prefer using the class's name directly.",
},
],
},
}
Banning specific uses of TypeScript enums
{
"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.",
},
],
},
}
Banning specific uses of TypeScript tuples
{
"rules": {
"no-restricted-syntax": [
"error",
// enforce tuple members have labels
{
"selector": "TSTupleType > :not(TSNamedTupleMember)",
"message": "All tuples should have labels.",
},
],
},
}
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.
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.
How does typescript-eslint compare to native-speed linters?
"Native-speed" linters such as Biome linter, Deno linter, and oxlint run in very fast languages such as Go or Rust, and so can generally run significantly faster than ESLint in non-type-aware-linting. They can also be easier to configure because they don't need to support the full ecosystem and legacy trail of ESLint.
However, until they support typed linting, they aren't a full replacement for typescript-eslint.
If you plan on using them, we recommend "dual linting": using those linters for what they support, then adding in ESLint for typed linting in a parallel or second step.
Our *-type-checked-only
Shared Configs will enable only type-checked rules in your ESLint config.
For example, the following config enables only the recommended config's type-checked rules with recommendedTypeCheckedOnly
:
- Flat Config
- Legacy Config
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
tseslint.configs.recommendedTypeCheckedOnly,
{
languageOptions: {
parserOptions: {
projectService: true,
tsconfigRootDir: import.meta.dirname,
},
},
},
);
module.exports = {
extends: ['plugin:@typescript-eslint/recommended-type-checked-only'],
parserOptions: {
projectService: true,
tsconfigRootDir: __dirname,
},
root: true,
};
For example, oxlint's Integration > ESLint documentation describes how to turn off ESLint rules that are already supported by oxlint.
"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're 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.