JavaScript FAQs
How do I use typescript-eslint in a JavaScript project?
typescript-eslint rules can run on JavaScript files, and many are useful even if you never write TypeScript code.
For example, no-deprecated reports uses of anything marked with a JSDoc @deprecated tag, and naming-convention enforces naming styles on any identifier.
Rules that match only TypeScript syntax, such as no-explicit-any, will not report on JavaScript files.
The setup is essentially the same as in Getting Started, just with files configured to include JavaScript file extensions.
You do still need to install typescript, as typescript-eslint loads TypeScript even when you're not using type-aware rules.
- npm
- Yarn
- pnpm
- Bun
npm install --save-dev eslint @eslint/js typescript typescript-eslint
yarn add --dev eslint @eslint/js typescript typescript-eslint
pnpm add --save-dev eslint @eslint/js typescript typescript-eslint
bun add --dev eslint @eslint/js typescript typescript-eslint
// @ts-check
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig({
files: ['**/*.{js,cjs,mjs,jsx}'],
extends: [js.configs.recommended, tseslint.configs.recommended],
});
How do I use typed linting in a JavaScript project?
Rules that need type information work in JavaScript files too.
Follow Linting with Type Information, then make sure your TSConfig includes your JavaScript files.
TypeScript leaves them out unless allowJs is enabled:
{
"compilerOptions": {
"allowJs": true
},
"include": ["src"]
}
Without that, every linted file reports a parsing error saying it "was not found by the project service".
In a JavaScript-only project, you can use a jsconfig.json file instead of a tsconfig.json, which implies allowJs by default.
Typed rules then work off the types TypeScript infers, including the ones it reads from JSDoc comments.
no-deprecated is a good example, as it needs no type annotations at all:
/** @deprecated Use `fetchUser` instead. */
export function getUser() {}
getUser();
~~~~~~~
// 4:1 - 4:8 `getUser` is deprecated. Use `fetchUser` instead. @typescript-eslint/no-deprecated
How do I use JSDoc types?
TypeScript reads JSDoc type tags in JavaScript files, and typescript-eslint rules work off the types it infers from them. No extra configuration is needed for that beyond the typed linting setup above:
// @ts-check
import js from '@eslint/js';
import { defineConfig } from 'eslint/config';
import tseslint from 'typescript-eslint';
export default defineConfig({
files: ['**/*.{js,cjs,mjs,jsx}'],
extends: [js.configs.recommended, tseslint.configs.recommendedTypeChecked],
languageOptions: {
parserOptions: {
projectService: true,
},
},
});
With that in place, annotating a parameter resolves the no-unsafe-* reports its implicit any would otherwise cause:
export function shout(value) {
return value.toUpperCase();
~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 2:10 - 2:27 Unsafe call of an `any` typed value. @typescript-eslint/no-unsafe-call
// 2:16 - 2:27 Unsafe member access .toUpperCase on an `any` value. @typescript-eslint/no-unsafe-member-access
// 2:3 - 2:30 Unsafe return of a value of type `any`. @typescript-eslint/no-unsafe-return
}
/** @param {string} value */
export function shoutTyped(value) {
return value.toUpperCase(); // Reports nothing
}
At the time of writing, JSDoc type assertions are not fully supported by typescript-eslint rules (#1682). TypeScript applies the asserted type, but typescript-eslint is currently unaware of these assertions, so rules still read the non-asserted type of the expression:
/** @param {any} value */
export function readPort(value) {
const config = /** @type {{ port: number }} */ (value);
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// 3:9 - 3:57 Unsafe assignment of an `any` value. @typescript-eslint/no-unsafe-assignment
return config.port;
}
Why JSDoc assertions aren't fully supported
A /** @type {T} */ (expression) assertion has no AST node of its own, so a rule asking for the type of that expression generally receives the type of expression rather than T.
It isn't a complete breakdown, though. TypeScript still attaches the right types to the nodes typescript-eslint does see:
const config = /** @type {{ port: number }} */ (value);
// ^? any
config.port;
// ^? number
JSDoc types will usually work, but are not supported by some rules that expect TypeScript syntax to be present.
For example, explicit-function-return-type and explicit-module-boundary-types report "Missing return type on function" on a function with a @returns tag, since they only look for genuine TypeScript type syntax.
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 files configuration to either:
- (recommended) only enable TypeScript-specific rules on TypeScript file extensions
- turn off TypeScript-specific rules on JavaScript-only file extensions
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.