tsconfig Audit
Purpose
This skill reports by default and writes only after the user approves it in Step 6. Nothing
before that step touches a file: Steps 1-5 read, run the checker, and print. An approved apply
edits tsconfig.json files inside the project and nothing else — no commit, no push, no
node_modules.
Report what a tsconfig.json should look like on TypeScript 7:
- Options and values TS7 rejects outright, with a migration for each
- Options that are redundant — locked to one value, implied by another option, or equal to a default
- Options that became load-bearing because a default changed, and must now be written explicitly
- Options that look droppable but should be kept
When to Use This Skill
Trigger phrases: "tsconfig audit", "tsconfig cleanup", "TypeScript 7 migration", "compilerOptions", "drop tsconfig options".
Scope
This audits compilerOptions only. include/exclude/files are read to reason about
rootDir, not audited. It does not touch dependency hygiene, lint config, or build scripts.
The target is always TypeScript 7. Advice for staying on 5.x or 6.x is out of scope — those configs are audited against what 7 will require of them.
How It Works
The compiler is the authority wherever it can answer, so the audit stays correct as TypeScript releases move:
| Source | Answers |
|---|---|
tsc --noEmit diagnostics |
Removals (TS5102/TS5108), unknown options (TS5023), invalid values (TS6046), illegal combinations (TS5095) |
tsc --showConfig |
Options implied by another option, and the fully resolved extends chain |
references/ts7-options.json |
Defaults, locked values, which unknown options are legacy removals rather than typos, keep-list |
Two things the compiler will not tell you, which the script handles itself:
--showConfigswallows config errors. It prints a config containing a removed option and exits 0. It is never used as a diagnostics source.- Diagnostics anchor to the leaf config, even when the option was inherited from a base inside
node_modules. The script walks theextendschain itself to attribute each option to the file that really set it.
Asking the User
Every question in this skill is written as AskUserQuestion options. Use that tool where
the host offers it, or the host's nearest structured-choice equivalent. Where the host has
neither, ask the same question in normal chat as a numbered list of 2–5 options —
recommended first, one short line of description each — and wait for the user to reply
with a number.
Workflow
Step 1: Find the configs
Unless the user named one, list the candidates before auditing anything:
# Glob: **/tsconfig*.json, excluding node_modules
A repo commonly has several — a root config plus build, site, test and benchmark variants, each
extending the root. Audit the one the user meant, and say which others exist rather than silently
auditing only the root. A config reached through extends is covered automatically as part of the
chain; a sibling config is not.
Close the step with one line: Configs: 4 found, auditing tsconfig.json (extends @tsconfig/node22), 3 siblings not audited.
Step 2: Run the checker
node <skill-dir>/scripts/tsconfig-check.mjs [path/to/tsconfig.json]
Add --json for structured findings when you need to post-process them. Use --tsc <path> to
point at a specific compiler.
The script needs a TypeScript compiler. It looks for node_modules/.bin/tsc upward from the
config, then tsgo, then tsc on PATH. If none is found it stops rather than guessing — do not
work around this by hand-reasoning about the config.
If the compiler found is older than 7.x, the script still runs and says so. Findings from the data file stay TS7-accurate, but removal and unknown-option findings come from the older compiler and will understate what TS7 rejects. Say this plainly in the report rather than presenting partial results as complete.
Step 3: Read the findings
Each finding carries option, action, reason, severity, confidence, sourceFile and
editable. With --json they arrive as a flat list — group them yourself; the grouping is a
presentation choice, not part of the data:
{
"option": "baseUrl",
"value": "./src",
"action": "remove",
"reason": "removed-in-ts7",
"severity": "error",
"confidence": "high",
"sourceFile": "tsconfig.json",
"editable": true,
"suggestion": "\"paths\": {\"*\": [\"./src/*\"]}"
}
Group them for the user by severity:
- Blocking —
severity: error. The build fails on TS7 until these are fixed. - Must add —
reason: conditional-add. Nothing errors; the output silently moves or globals silently vanish. Explain the concrete consequence, not just the flag. - Safe to drop —
locked-value,implied-by,matches-default. - Keep —
keep-list,external-consumer.
Two flags change what you can offer:
editable: falsemeans the option lives outside the project (typically an@tsconfig/*base innode_modules). Never edit it. Offer a local override instead, and say which package owns it.needsResearch: truemeans TS7 reported an unknown option that is not a known legacy removal. Go to Step 4.
Step 4: Research unknown options
An unrecognised option is one of three things, and they have opposite fixes:
- a typo — suggest the nearest real option from
knownOptionsin the data file - a third-party extension (
ts-node,tsc-alias,@vue/tsconfig) — a sibling key likets-nodeis legal and must be left alone; onlycompilerOptionsentries are audited - newer than the data file — a genuine option added after
tsVersion
Grep the repo for the option name and for any tool that would own it — a ts-node or tsc-alias
dependency in package.json settles the second case without a web lookup. Otherwise check the
current TypeScript release notes and the option reference for the name before advising.
Then report it to the user as a gap: name the option, say which case it was, and propose the
concrete references/ts7-options.json change that would let the checker classify it next time.
Do not silently recommend deleting an option you could not identify.
Step 5: Report
Lead with the blocking findings and the concrete failure each one causes. Then the must-add findings, then the safe drops as a single grouped list, then anything to keep and why.
Close with a Not checked list, one line per item with its reason. Everything the run could not
cover goes there — sibling configs from Step 1, the configs behind project references, an option
Step 4 could not identify, and any finding class the compiler could not produce because the tsc
found was older than 7.x. State the compiler and data-file versions the audit ran against; if they
differ, that is a Not checked line too. A report without this section reads as a full solution
audit.
A filled report:
## tsconfig audit — tsconfig.json (tsc 6.4.2, data file TS 7.0.2)
### Blocking (2)
- `baseUrl: "./src"` — removed in TS7 (`TS5102`); the build stops before typechecking.
Fold the prefix into `paths`: `"paths": { "*": ["./src/*"] }`.
- `moduleResolution: "node"` — value removed in TS7 (`TS6046`). Use `"bundler"`; this project
is built by Vite, so no `.js` extensions have to be added to relative imports.
### Must add (1)
- `target` is unset. In TS7 the default floats to the newest stable ECMAScript version, so the
emitted syntax changes the day the compiler is upgraded. Pin `"target": "es2023"` — what the
current compiler already emits.
### Safe to drop (4)
`esModuleInterop`, `allowSyntheticDefaultImports` (implied by it), `skipDefaultLibCheck`
(implied by `skipLibCheck`), `forceConsistentCasingInFileNames` (matches the TS7 default).
Cleanup, not required work — defaults can move between releases.
### Keep (2)
- `strict: true`, `module: "esnext"` — keep-list; both are load-bearing on the TS7 defaults.
- `declaration: true` — `external-consumer`: this package ships `types` in `package.json`.
### Not checked
- `tsconfig.build.json`, `tsconfig.test.json`, `site/tsconfig.json` — siblings, not reached
through `extends`. Offer to audit each.
- Removal and unknown-option findings came from tsc 6.4.2, not 7.x, so they understate what TS7
rejects. Re-run against a 7.x compiler to close this gap.
- `tsc-alias` sibling key — not a `compilerOptions` entry; out of scope, left alone.
Step 6: Offer to apply
Ask before writing, per Asking the User:
- Apply blocking fixes and safe drops — everything except the keep findings
- Apply blocking fixes only — the minimum to build on TS7
- Report only — change nothing
When applying:
- Apply one targeted edit per option. Remove or change the specific lines and nothing else.
Never rewrite or reserialize the whole file:
tsconfig.jsonis JSONC and routinely commented, and a reserialize strips every comment and reorders every key. - Only edit files inside the project. For
editable: falsefindings, add a local override. - Capture a baseline first:
tsc -p <config> --noEmit --locale enbefore any edit. - Verify after: rerun it and compare the set of diagnostics, not the count — an equal count can hide a different failure.
- When the config emits, also compare emitted paths before and after
(
tsc -p <config> --listFilesOnlyplus the resolvedoutDirlayout). This is the only check that catches arootDirregression, which--noEmitstructurally cannot see. - If verification regresses, revert your own edits and report what happened. Do not leave the config half-migrated.
Then stop. Report what was applied and what verification said; do not audit the sibling configs, do not commit, and do not start a second apply round on findings the chosen option excluded.
Cautions
baseUrlandpathsare not tsc-only. Bundlers, Vitest/Jest and IDEs resolve them independently. A clean typecheck after dropping them proves nothing about runtime resolution. WhenbaseUrlis removed, every relativepathsentry needs its prefix folded in — the checker computes the rewrite.target,module,libandstrictare on the keep-list on purpose. The TS7targetdefault floats to the newest stable ECMAScript version, so dropping it makes emit change on a compiler upgrade.- "Matches the default" is a weaker claim than "removed". Defaults can move between releases; a removal will not come back. Present drops as cleanup, not as required work.
- Never recommend
ignoreDeprecationsas a fix. It did nothing in TS7 — removed is removed.
Keeping the Data File Current
references/ts7-options.json is generated. Do not hand-edit it.
node <skill-dir>/scripts/refresh-options.mjs [--tag typescript/v7.0.2] [--tsc <path>]
It pulls the compiler's own option declarations from the pinned microsoft/typescript-go release
tag, then probes an installed tsc for removals, enums and implications. Probes win where the two
disagree — the declared defaults are help strings and are wrong in places. rootDir declares
"Computed from the list of input files" but is fixed at ., and the 7.0 release notes claim
stableTypeOrdering cannot be disabled while the compiler accepts false.
Regenerate when a new TypeScript release ships, or when Step 4 turns up an option the data file does not know.
Reference Files
references/migrations.md— the fix for each removed option and valuereferences/ts7-options.json— generated option data