Your tsconfig.json says "strict": true, so you're safe โ right? Except
noUncheckedIndexedAccess (the flag that makes arr[i] return T | undefined)
isn't part of strict, your module / moduleResolution are a mismatched
pair that mis-resolves ESM imports, allowJs is on but checkJs is off so your
JS is never type-checked, and the strict flag you think you have is actually
turned back off three files up the extends chain.
TypeScript has 100+ compiler options and no tool that grades your config.
tsconfig-doctor resolves the whole extends chain into the effective config,
scores its strictness, and flags every missing safety flag and contradiction โ
locally, deterministically, with the exact fix.
npx tsconfig-doctor scantsconfig.json 53/100 (F) ยท strictness 4%
โ `strict` mode is off strict
โ noUncheckedIndexedAccess is off noUncheckedIndexedAccess
โ L6 Modern `module` with legacy `moduleResolution` footgun.module-resolution
โ L7 allowJs is on but checkJs is off footgun.allowjs-no-checkjs
โน L4 Legacy compile target (es5) footgun.legacy-target
- ๐งฎ It grades your config, not a preset.
@tsconfig/strictestis a preset to copy;tsc --showConfigdumps the effective config with no judgment. Neither tells you which safety flags you're missing or which settings contradict. - ๐ It follows
extends. Relative paths,extendsarrays (TS 5.0+), and packages like@tsconfig/strictestโ resolved throughnode_modulesinto one effective config, so it catches a strict flag disabled in a base you inherited. - ๐ฏ Every finding has the exact fix. Not "consider strictness" โ the precise option to add and why it matters (with the failure it prevents).
- ๐ Local & deterministic. No network, no API key. Drop it in CI and the build
fails the moment a PR weakens
strictor dropsnoUncheckedIndexedAccess.
Why not ask an LLM "is my tsconfig strict"? It can't resolve your real extends
chain across node_modules, can't run on every PR, and option interactions
(strict โ 8 flags, module/moduleResolution pairing) are exact rules, not a
guess.
# run it now, from your project root
npx tsconfig-doctor scan
# or add it
npm install -g tsconfig-doctor # global CLI
npm install -D tsconfig-doctor # CI dependencyNode โฅ 18. With no arguments it audits ./tsconfig.json.
tsconfig-doctor scan # audit ./tsconfig.json
tsconfig-doctor scan packages/app/tsconfig.json # a specific file
tsconfig-doctor scan --min-score 80 # CI gate
tsconfig-doctor scan --md tsconfig-report.md # Markdown for a PR comment
tsconfig-doctor scan --allow noUnusedLocals,noUnusedParameters
tsconfig-doctor init # write a configSee examples/sample-report.md, and
examples/strict.tsconfig.json for a config that
scores an A.
| Group | Examples |
|---|---|
| Strictness | strict and its 8 family flags (strictNullChecks, noImplicitAny, โฆ) โ including any you disabled under strict |
| Extra safety | noUncheckedIndexedAccess (the big one strict omits), noImplicitReturns, noFallthroughCasesInSwitch, noImplicitOverride, exactOptionalPropertyTypes, unused locals/params |
| Interop & modules | esModuleInterop, forceConsistentCasingInFileNames, isolatedModules, verbatimModuleSyntax |
| Footguns | module/moduleResolution mismatch, allowJs without checkJs, noEmit with declaration/composite, composite without declaration, verbatimModuleSyntax vs deprecated flags, legacy target |
Each finding is a weighted error / warning / info; the file rolls up to a 0โ100 score, an AโF grade, and a strictness % (how much of the recommended rubric weight you've achieved).
1. Stop strictness regressions in CI. A PR that flips strict off, or disables
strictNullChecks "just to ship", fails the gate:
# .github/workflows/ci.yml
- run: npx tsconfig-doctor scan --min-score 902. Audit a monorepo's packages at once. Point it at every package tsconfig and see which ones quietly inherit a loose base:
tsconfig-doctor scan packages/*/tsconfig.json --md report.md3. Harden a project you inherited. npx tsconfig-doctor scan gives you a
prioritized checklist โ the exact flags to add, hardest-hitting first.
tsconfig-doctor init writes tsconfig-doctor.config.json:
import { analyzeTsconfigFile, resolveTsconfig, DEFAULT_CONFIG } from "tsconfig-doctor";
const report = analyzeTsconfigFile("tsconfig.json", DEFAULT_CONFIG);
console.log(report.score, report.strictness, report.findings);
// Or just resolve the effective config yourself:
const { options } = resolveTsconfig("tsconfig.json");Also exported: analyzeResolved (pure), RUBRIC, STRICT_FAMILY, parseJsonc,
and types.
- ๐ค Optional
--ailayer (bring-your-own key) to explain a finding's impact on your code or draft the migration. The core stays 100% offline and deterministic. - A
--fixmode that writes the safe, additive flags into your tsconfig. - TS-version-aware defaults (some defaults changed across releases).
- More footguns (
pathswithoutbaseUrl,jsxwithoutjsxImportSource, lib/target mismatch). - A web playground โ paste a tsconfig, see the audit, nothing uploaded.
tsconfig-doctor is free and MIT-licensed, built and maintained in spare time. If it hardened your types, please consider supporting it:
- โญ Star this repo โ the simplest free way to help others find it.
- ๐ Sponsor via Lemon Squeezy โ one-time or recurring.
MIT ยฉ tsconfig-doctor contributors
{ "ignore": [], // rule/option ids to skip entirely "allow": [], // options you've intentionally left off (e.g. ["noUnusedLocals"]) "minScore": 0 // CI gate threshold }