M Logo
๐Ÿ“ TypeScript from Zero ยท Part 48 of 50

TypeScript Linting: ESLint Flat Config, typescript-eslint and TS 7 (ERESOLVE Fix)

November 6, 2026

Welcome to episode 48 of TypeScript from Zero, a series for complete beginners (no JavaScript needed). Welcome back to TypeScript from zero! tsc finds type errors. A linter finds other problems: code that is legal, but likely to be a bug, or hard to read. The standard tool is ESLint, with a plugin for TypeScript. Today we set it up, and we run into a real surprise on the way. We start in a new file called lint demo.ts. Every code block below is the exact code and output from the video, ready to copy.

Setup

We start in the terminal and open the project in VS Code:

bash
cd tasks
touch src/lint-demo.ts
code .

1. Install

We install ESLint, its recommended rules, and typescript eslint. And npm stops with an error: could not resolve. The reason is in the message: typescript eslint needs a TypeScript version below six point one. We have version seven, the new, fast compiler. The tool does not support it yet, because it needs the old JavaScript API of the compiler.

text
$ npm install --save-dev eslint @eslint/js typescript-eslint 2>&1 | tail -12
npm error
npm error Could not resolve dependency:
npm error peer typescript@">=4.8.4 <6.1.0" from typescript-eslint@8.72.1
npm error node_modules/typescript-eslint
npm error   dev typescript-eslint@"*" from the root project
npm error
npm error Fix the upstream dependency conflict, or retry this command with --force or --legacy-peer-deps to accept an incorrect (and potentially broken) dependency resolution.
npm error
npm error
npm error For a full report see:
npm error /home/mah/.npm/_logs/2026-11-06T10_14_04_215Z-eresolve-report.txt
npm error A complete log of this run can be found in: /home/mah/.npm/_logs/2026-11-06T10_14_04_215Z-debug-0.log

2. Side by side

The TypeScript team has an answer: run both versions side by side. The name typescript points to the old version six, which is used only by tools like this one. And the new compiler gets another name, at typescript slash native, which still gives us the tsc command. Now the install works. And tsc is still version seven.

text
$ npm install --save-dev typescript@npm:@typescript/typescript6 '@typescript/native@npm:typescript@^7.0.2' && npm install --save-dev eslint @eslint/js typescript-eslint && npx tsc -v

added 2 packages, changed 1 package, and audited 47 packages in 2s

13 packages are looking for funding
  run `npm fund` for details

found 0 vulnerabilities

added 91 packages, and audited 138 packages in 43s

41 packages are looking for funding
  run `npm fund` for details

10 moderate severity vulnerabilities

To address all issues (including breaking changes), run:
  npm audit fix --force

Run `npm audit` for details.
Version 7.0.2

3. Config

ESLint reads a flat config file, a normal JavaScript module that exports a list. We take the basic recommended rules, and the type checked rules of typescript eslint. And one setting: project service true, which lets the rules ask the compiler about types. That is what makes the rules smart.

4. Bad code

Now a file with two habits to avoid. A variable of type any, which switches the checks off. And a call to an async function without await, a floating promise: if it fails, nobody will ever know. We run ESLint on the file. It reports the any, the unsafe use of it, and the promise that must be awaited. That last rule needs types: ESLint has to know that main returns a promise.

eslint.config.mjs:

ts
import eslint from "@eslint/js";
import tseslint from "typescript-eslint";

export default tseslint.config(
  eslint.configs.recommended,
  tseslint.configs.recommendedTypeChecked,
  {
    languageOptions: {
      parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname },
    },
  },
);

src/lint-demo.ts:

ts
import { loadTasks } from "./store.ts";

async function main() {
  const data: any = await loadTasks();
  console.log(data.length);
}
main();
text
$ npx eslint src/lint-demo.ts

/home/mah/tasks/src/lint-demo.ts
  4:15  error  Unexpected any. Specify a different type                                                                                                                            @typescript-eslint/no-explicit-any
  5:20  error  Unsafe member access .length on an `any` value                                                                                                                      @typescript-eslint/no-unsafe-member-access
  7:1   error  Promises must be awaited, end with a call to .catch, end with a call to .then with a rejection handler or be explicitly marked as ignored with the `void` operator  @typescript-eslint/no-floating-promises

โœ– 3 problems (3 errors, 0 warnings)

5. Fix

We remove the any, so the type comes from load tasks, and we put the await in front of main. ESLint is quiet, and so is tsc.

src/lint-demo.ts:

ts
import { loadTasks } from "./store.ts";

async function main() {
  const data = await loadTasks();
  console.log(data.length);
}
await main();
bash
$ npx eslint src/lint-demo.ts
bash
$ npx tsc --noEmit
bash
$ npx eslint src/lint-demo.ts && npx tsc --noEmit

Recap

Today: ESLint with the flat config, typescript eslint with type checked rules, the floating promise rule, and the workaround for TypeScript seven: the old version under the name typescript, and the new one as native. Next episode: project references.

ยฉ 2026 All Rights Reserved By Mahmudul Alam