Skip to Content
Course content

54: Integrating TypeScript with ESLint

Click on the "Edit" button in the top corner of the screen to edit your slide content.

One of the first things I see when I onboard a new dev to a project is a .eslintrc file that looks like it was copy-pasted from a 2018 blog post. Usually, they've got ESLint installed, but it's fighting with TypeScript. You'll see those annoying red squiggles in the IDE that aren't coming from the compiler, but from a linter that doesn't actually understand what an interface is. It's frustrating, and usually, the reaction is to just turn off the rules that are complaining. Don't do that.

The struggle of treating TypeScript like JavaScript

The naive way to set this up is to treat TypeScript as just "JavaScript with some extra noise." In this scenario, you install ESLint and maybe a few basic plugins, but you leave the parser as the default. Because the default ESLint parser only knows standard JavaScript, it hits a type annotation or a generic and essentially panics. To "fix" this, people often start adding eslint-disable comments everywhere or disabling rules that seem to trigger incorrectly on TS files.

Even if you get the basic @typescript-eslint/parser installed, most people stop there. They get "syntax-aware" linting, which is fine for catching unused variables or formatting issues. But they're missing the real magic. For example, consider this snippet:

async function getUserData(id: string) {
  const user = await fetchUserFromCache(id); 
  return user;
}

function fetchUserFromCache(id: string) {
  return { id, name: "Alex" }; // This is a synchronous function!
}

If you're just using basic linting, ESLint sees the await keyword and thinks, "Yep, that's valid JavaScript syntax." TypeScript's compiler also won't stop you; it'll just implicitly wrap the result in a Promise. But from a code quality perspective, this is a "smell." You're awaiting something that isn't a promise, which adds unnecessary overhead and signals a misunderstanding of the data flow.

The power of type-aware linting

The better way is to implement "type-aware linting." This is where we tell ESLint not just how to parse the code, but how to actually ask the TypeScript compiler about the types of the variables it's looking at. This requires a bit more configuration in your parserOptions—specifically, you have to point ESLint to your tsconfig.json.

When you do this, you unlock a whole different class of rules. In the example above, a type-aware rule like @typescript-eslint/await-thenable would immediately flag that await fetchUserFromCache(id) is useless because fetchUserFromCache returns a plain object, not a Promise. I love this because it catches logic errors that are technically "legal" TypeScript but are almost certainly bugs or remnants of a refactor gone wrong.

The cost of the "Full" setup

I should be honest with you: there is a trade-off here. Type-aware linting is significantly slower. Why? Because ESLint now has to essentially run a partial TypeScript compilation in the background to understand the types of your expressions. On a small project, you won't notice. On a massive monorepo with thousands of files, your IDE might start to lag, or your CI pipeline might take an extra few minutes.

If you find the performance hit is too high, the professional compromise is to split your linting. You run the "cheap" syntax rules on every file save, but you run the "expensive" type-aware rules as a pre-commit hook or during your CI build. It's a balance between developer velocity and absolute correctness. Personally, I'd rather wait an extra 30 seconds in CI than spend an hour debugging why a "synchronous" function is behaving like an async one.




📋 Practical Task

Exercise: Fixing the Order Processing Service

You have been handed a small project containing an OrderProcessingService.ts file. The project has ESLint and TypeScript installed, but it is currently configured for "syntax-only" linting. The compiler is happy, but the code is sloppy.

Your goals:

  • Modify the .eslintrc.js (or .eslintrc.json) to enable type-aware linting by adding the project: true (or path to tsconfig.json) inside parserOptions.
  • Enable the @typescript-eslint/no-floating-promises and @typescript-eslint/await-thenable rules.
  • Identify and fix the three linting errors that appear in OrderProcessingService.ts:
    1. An await call on a function that returns a non-promise value.
    2. A Promise-returning function called without a .catch() or an await (a floating promise).
    3. An unnecessary type cast (e.g., as any) that the linter can now prove is redundant because it knows the actual type.
Rating
0 0

There are no comments for now.

to be the first to leave a comment.