ShopperCove
Menu
All writingBlogTopicsCategoriesAboutRSS
Blog
Categories
Observability & SRE62All categories
Topics
sre111localhost lab102performance70python58javascript47bundle size36benchmarking35node.js35web development21typescript19benchmark16content creation15All topics
About
Loading article…

ShopperCove

Hands-on notes on developer tools, written after the tool has actually been run.

All writingBlogCategoriesTopicsAboutPrivacyRSS

© 2026 ShopperCove

On this page

  1. What is the difference between recommended and strict-type-checked?
  2. How did we measure it on a real repo?
  3. What did each preset find?
  4. Are the strict-only findings real bugs?
  5. Is type-aware linting the real cost?
  6. How do you switch presets in a flat config?
  7. How can you adopt strict-type-checked without a 104-error PR?
  8. Which preset should your team pick?
  9. FAQ
  10. Related
  11. Sources
  1. Blog

Is typescript-eslint Strict Worth 4x Errors?

We ran three typescript-eslint presets on a real open-source TypeScript library and counted every finding and second. The jump to strict was smaller than the jump to type-aware linting, and the findings themselves tell you whether your team is ready.

4 November 2026·7 min read

Summary

Disclosure

This post contains affiliate links. If you buy through them, I may earn a commission at no extra cost to you. I only make firsthand claims about products I have actually used.

On this page
  1. What is the difference between recommended and strict-type-checked?
  2. How did we measure it on a real repo?
  3. What did each preset find?
  4. Are the strict-only findings real bugs?
  5. Is type-aware linting the real cost?
  6. How do you switch presets in a flat config?
  7. How can you adopt strict-type-checked without a 104-error PR?
  8. Which preset should your team pick?
  9. FAQ
  10. Related
  11. Sources

typescript-eslint recommended vs strict-type-checked: which one should your project use? We ran both, plus recommended-type-checked in between, on the real ky HTTP library. recommended found 25 issues in 0.89 s. strict-type-checked found 104 in 2.09 s. Most of the extra time comes from type information, not from strictness.

That 4x jump in findings sounds scary. But when we read through them, a lot were style opinions rather than bugs, and a few were real signals. Here is exactly what we ran, what each preset caught, and how to choose without drowning your team in red squiggles.

What is the difference between recommended and strict-type-checked?

The typescript-eslint docs describe a ladder of shared configs:

  • recommended: rules for code correctness that don't need type information. It's fast and the safe default.
  • recommended-type-checked: recommended plus rules that use the TypeScript type checker, such as the no-unsafe-* family.
  • strict: recommended plus more opinionated rules that may also catch bugs.
  • strict-type-checked: contains recommended, recommended-type-checked and strict, plus strict type-aware rules. Some shared rules also get stricter options.

Two warnings from the docs matter here. First, the strict configs are not considered stable under semver, so a minor typescript-eslint update can add rules and new errors. Second, the docs suggest strict only if "a nontrivial percentage" of your developers are "highly proficient in TypeScript".

On typescript-eslint 8.72.1, we counted how many @typescript-eslint/ rules each preset turns on: recommended 20, recommended-type-checked 43, strict 28 and strict-type-checked 70.

How did we measure it on a real repo?

  • Repo: sindresorhus/ky at commit 3541888 (9 October 2026), package version 2.2.0. That's 31 .ts files and 4,917 lines in source/. tsc --noEmit passed cleanly before we started.
  • Tools: typescript-eslint 8.72.1 (npm latest, published 26 October 2026), ESLint 9.39.5, TypeScript 5.9.3, Node.js 24.22.1, on an 8-core x86_64 Linux box.
  • Configs: three flat config files, each using tseslint.config() scoped to source/**/*.ts. The two type-checked ones enable parserOptions.projectService: true, as the typed-linting guide recommends.
  • Noise control: ky has 28 inline eslint-disable comments written for xo and unicorn rules. We turned off unused-directive reporting and excluded three "rule definition not found" messages for unicorn rules, because they come from the setup, not from the presets.
  • Timing: five cold runs of npx eslint -c <config> source -f json per preset, reporting the median.

What did each preset find?

PresetFindingsFiles with findingsMedian time (5 runs)
recommended2530.89 s
recommended-type-checked7561.94 s
strict-type-checked104152.09 s

recommended (25): no-explicit-any 14 times (9 in merge.ts, 4 in type-guards.ts, 1 in Ky.ts), no-unused-vars 10 times (all in Ky.ts) and prefer-const once.

recommended-type-checked (+50): the type checker turned up the unsafe-any chain. That's no-unsafe-member-access 23, no-unsafe-assignment 12, no-unsafe-return 4 and no-unsafe-argument 4. There were also 5 no-unnecessary-type-assertion hits, all auto-fixable, plus 1 require-await and 1 unbound-method. Most of the unsafe-any findings trace back to the same any-typed helpers that recommended had already flagged in merge.ts and type-guards.ts. Fix the source any, and many of these go away together.

strict-type-checked (+29):

  • no-unnecessary-condition 11. For example, "Unnecessary optional chain on a non-nullish value" in Ky.ts, and a check flagged as "always falsy" in constants.ts.
  • no-invalid-void-type 10, mostly hook types like Request | Response | void | Promise<...> in hooks.ts.
  • no-non-null-assertion 4, such as this.#abortController!.signal in Ky.ts.
  • restrict-template-expressions 3, flagging numbers placed in template strings in error messages.
  • no-unnecessary-type-parameters 1.

Are the strict-only findings real bugs?

Mostly not, in this repo, and that's the honest answer. ky deliberately uses void in hook return types so that hooks can return nothing. Putting a status-code number in an error message template is intended. The four non-null assertions sit on fields the code sets up before use. A strict preset flags these patterns because they can hide bugs, not because they always do.

The no-unnecessary-condition hits are the most interesting group. Each one says TypeScript believes a check can never fail. That means either the check is dead code or the types are looser than the runtime reality, and both are worth a look. In our run, that was the most useful rule strict-type-checked added.

Is type-aware linting the real cost?

Yes. Going from recommended to recommended-type-checked more than doubled the median time, from 0.89 s to 1.94 s, and tripled the findings. Going on to strict-type-checked added only 0.15 s more on our box. That makes sense: building the TypeScript program is the expensive part, and once it exists, more rules are cheap. On a 4,917-line library the difference is only seconds. On a large monorepo the type-aware step is the one to watch in CI.

How do you switch presets in a flat config?

This is the shape of the strict-type-checked config we ran. It follows the typed-linting guide:

// eslint.config.mjs
import tseslint from 'typescript-eslint';

export default tseslint.config({
  files: ['source/**/*.ts'],
  linterOptions: { reportUnusedDisableDirectives: 'off' }, // only for this test, see above
  extends: [tseslint.configs.strictTypeChecked],
  languageOptions: {
    parserOptions: {
      projectService: true,
      tsconfigRootDir: import.meta.dirname,
    },
  },
});

For the other two runs we swapped strictTypeChecked for recommendedTypeChecked, or for recommended with the parserOptions block removed, because recommended doesn't need type information. You can also stack stylisticTypeChecked on top, but we left it out because it's about style, not correctness.

How can you adopt strict-type-checked without a 104-error PR?

Based on our run, the cheapest order is:

  1. Fix the any sources first. On ky, 14 no-explicit-any hits were the upstream cause of most of the 43 no-unsafe-* findings. Typing those helpers as unknown and narrowing them clears findings in several rules at once.
  2. Apply the auto-fixes. eslint --fix handled the 5 no-unnecessary-type-assertion findings, the only fixable ones in our run.
  3. Turn on strict-type-checked, then turn off the opinionated rules you disagree with. For example, set @typescript-eslint/no-invalid-void-type to off if your API intentionally uses void in unions, as ky's hooks do. That one change would have removed 10 of the 29 strict-only findings.
  4. Keep no-unnecessary-condition. In our run it found checks that the types say can never fail, and those are the ones worth reading.

Which preset should your team pick?

  • Starting out, or a mixed-skill team: use recommended, or better, recommendedTypeChecked. On our run the type-aware step found the any leaks, which are the most bug-prone pattern.
  • TypeScript-heavy team: turn on strictTypeChecked, but add it in a separate PR, fix or disable rules one at a time, and pin your typescript-eslint version so a minor update can't add surprise errors.
  • Already using another linter set (as ky does with xo): don't stack presets blindly. Pick the type-aware rules you're missing.

If you're also considering other linters, see oxlint vs ESLint and Biome vs ESLint + Prettier. For a rule that recently tightened, our page on typescript-eslint 8.71 and no-unsafe-enum-assignment shows how a single release can change your error count.

FAQ

Does strict-type-checked include recommended? Yes. It contains recommended, recommended-type-checked and strict.

Is strict-type-checked stable across minor versions? No. The docs say the strict configs are not semver-stable, so pin your version.

Why is type-checked linting slower? It builds a TypeScript program first. On our run that took the median from 0.89 s to 1.94 s.

Can I use strict without type checking? Yes. The strict preset needs no type information.

What did strict-type-checked add on ky? 29 findings beyond recommended-type-checked, led by no-unnecessary-condition (11) and no-invalid-void-type (10).

Related

  • 01shoppercove.comWhat did typescript-eslint 8.71 change for enum assignments?
  • 02shoppercove.comIs oxlint fast enough to replace ESLint?
  • 03shoppercove.comShould you upgrade to TypeScript 7?

Sources

  • 01typescript-eslint.io/users/configs/
  • 02typescript-eslint.io/getting-started/typed-linting/
  • 03npmjs.com/package/typescript-eslint
  • 04github.com/sindresorhus/ky
  • 05github.com/sindresorhus/ky/commit/3541888

Lab evidence

What I found running this

Measured on 3 Nov 2026 on our box (x86_64, 8 cores, Node v24.22.1). We used sindresorhus/ky at commit 3541888 (package version 2.2.0, 31 .ts files and 4,917 lines in source/), with typescript-eslint 8.72.1, ESLint 9.39.5 and TypeScript 5.9.3. We timed 5 runs of each preset and report the median. ky lints with xo in real life, not these presets, so many findings are deliberate choices rather than bugs.

Explore the product

Freelancers and devs who want to turn coding skills into paid site builds

Put your strict TypeScript skills to paid use

See Ready Client Sites

Notes when a lab post goes up

Occasional email for new hands-on reviews. No sequence and no sponsors.

Explore the product

Freelancers and devs who want to turn coding skills into paid site builds

Put your strict TypeScript skills to paid use

See Ready Client Sites

Keep reading

  • Python partition vs split: Which One Should You Use?

    Both cut a string at a separator, but they fail in different ways. We ran them on Python 3.13.5 to show where partition() is safer and where split() is the right tool.

    Summary2 Nov 20263 min read

  • toSorted vs sort JavaScript: Which One Copies Your Array?

    What changes when you swap sort() for toSorted(), with a real 1-million-item Node.js benchmark and the support table.

    Summary1 Nov 20263 min read

  • How to Reorder a YouTube Playlist, and Why Won't It Drag?

    Dragging videos is the official way to change a playlist's order. Here are the steps from YouTube Help, where to find them in YouTube Studio, and the cases where reordering will not work.

    Summary31 Oct 20263 min read