ts-tsconfig-modern-strict-starter
$
npx mdskill add kongyo2/agent-primary-ts-starters/ts-tsconfig-modern-strict-starterConfigure modern TypeScript with strict rules for reliable agent feedback.
- Enforces ESNext targets and maximum strictness to catch silent bugs.
- Uses tsc --noEmit as the primary feedback loop for code edits.
- Selects bundler or nodenext resolution based on project runtime needs.
- Applies standing policy automatically when tsconfig fields are discussed.
SKILL.md
.github/skills/ts-tsconfig-modern-strict-starterView on GitHub ↗
---
name: ts-tsconfig-modern-strict-starter
description: Set up a TypeScript tsconfig.json with modern (ESNext + bundler/nodenext) and maximally strict settings, tuned so that `tsc --noEmit` becomes a fast, reliable inner feedback loop for the LLM agent doing the editing. Use whenever a tsconfig.json is being created, edited, reviewed, troubleshot, or even just discussed — apply the standing policy even when the user only asks about a single option. Default to this skill instead of free-recall whenever the answer involves a tsconfig field.
---
# TS + npm tsconfig Modern Strict Starter (Agent-Friendly)
Use whenever a `tsconfig.json` is being created, edited, reviewed, or troubleshot in a TypeScript npm project. In a monorepo, edit the workspace the user names — ask if it isn't named.
The standing policy below is tuned so that **`tsc --noEmit` is the LLM agent's primary inner feedback loop**. Modern syntax + maximum strictness narrows the surface where an agent can ship a silent bug; explicit module resolution removes whole classes of "why isn't this importing" turns that would otherwise burn agent context; `incremental: true` keeps the loop fast enough to run between each edit.
## Standing Policy (override only with a stated reason)
- `"target": "esnext"`. Downleveling is the bundler / runtime's job, not tsc's.
- `"strict": true` plus every recommended add-on flag below.
- `"moduleResolution": "bundler"` for app code via a bundler; `"nodenext"` for code running on Node directly.
- `"skipLibCheck": true`. Always — a single broken `.d.ts` in `node_modules` otherwise stops the entire feedback loop.
- `"incremental": true` (or `"composite": true` under project references). The `.tsbuildinfo` cache makes the second-and-onward `tsc --noEmit` run near-instant, which is what keeps the agent loop tight enough to run between edits.
- `"isolatedModules": true` and `"verbatimModuleSyntax": true` for any code touched by esbuild / SWC / Babel / Vite.
When the user proposes loosening any of these, ask what concrete problem they are solving before agreeing.
## Pick the Use Case First
Resolve the use case **before** writing or modifying a tsconfig — the four templates differ in incompatible ways.
```
Is tsc emitting JS?
├── No, a bundler emits ─────────────────► §1 Browser app + bundler
└── Yes, tsc emits
├── Running on Node directly (ESM) ──► §2 Node ESM app
└── Publishing to npm
├── Single package ────► §3 Library
└── Monorepo ────► §4 Project references
```
## The `target` / `module` / `moduleResolution` Trio
Co-dependent. Pick one row, don't improvise.
| Scenario | `target` | `module` | `moduleResolution` |
| ------------------------------------- | --------- | ------------ | ------------------ |
| Browser app via bundler | `esnext` | `esnext` | `bundler` |
| Node.js native ESM | `esnext` | `nodenext` | `nodenext` |
| Publishable library (build via tsc) | `es2020`+ | `esnext` | `bundler` |
| Node.js CommonJS (legacy) | `es2022` | `commonjs` | `node` |
Common mismatches:
- `module: "commonjs"` + `moduleResolution: "bundler"` — invalid combination.
- `nodenext` + extensionless relative imports — Node ESM requires `.js` on relative imports even when the source is `.ts`.
## §1. Browser app + bundler (Vite, Next.js, Rspack, etc.)
```json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["esnext", "dom", "dom.iterable"],
"jsx": "react-jsx",
"noEmit": true,
"incremental": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"resolveJsonModule": true,
"allowImportingTsExtensions": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"allowUnreachableCode": false,
"allowUnusedLabels": false
},
"include": ["src"]
}
```
Drop `"dom"` and `"dom.iterable"` from `lib` for non-browser code. Change `jsx` to `"preserve"` plus `jsxImportSource` for Preact / Solid.
## §2. Node.js ESM application
`package.json` must have `"type": "module"`. Relative imports require `.js` even when the source is `.ts`.
```json
{
"compilerOptions": {
"target": "esnext",
"module": "nodenext",
"moduleResolution": "nodenext",
"lib": ["esnext"],
"outDir": "./dist",
"rootDir": "./src",
"sourceMap": true,
"incremental": true,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"resolveJsonModule": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["**/*.test.ts", "**/*.spec.ts", "dist"]
}
```
## §3. Publishable library
```json
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "bundler",
"rootDir": "./src",
"outDir": "./dist",
"declarationDir": "./dist-types",
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"incremental": true,
"allowImportingTsExtensions": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"skipLibCheck": true,
"isolatedModules": true,
"verbatimModuleSyntax": true,
"strict": true,
"noUncheckedIndexedAccess": true,
"exactOptionalPropertyTypes": true,
"noImplicitOverride": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"noUnusedLocals": true,
"noUnusedParameters": true
},
"include": ["src/**/*"],
"exclude": ["**/*.test.ts", "**/*.spec.ts"]
}
```
When publishing, also configure `package.json` `exports` — the `types` field must come first inside each conditional block.
## §4. Monorepo with project references
Root `tsconfig.json`:
```json
{
"files": [],
"references": [
{ "path": "./packages/core" },
{ "path": "./packages/utils" },
{ "path": "./packages/app" }
]
}
```
Each package extends a shared base and sets `composite: true`:
```json
{
"extends": "../../tsconfig.base.json",
"compilerOptions": {
"composite": true,
"rootDir": "./src",
"outDir": "./dist",
"declaration": true,
"declarationMap": true
},
"include": ["src/**/*"],
"references": [{ "path": "../core" }]
}
```
Build with `tsc --build` (or `tsc -b`). Plain `tsc` against the root won't resolve references.
## Strict Flags Beyond `strict: true`
These are not enabled by `strict: true` but should be on by default for an agent-primary workflow:
- **`noUncheckedIndexedAccess`** — `arr[i]` and `obj[k]` become `T | undefined`. Catches a class of silent index bugs that agents emit easily.
- **`exactOptionalPropertyTypes`** — `?:` no longer accepts an explicit `undefined`.
- **`noImplicitOverride`** — subclass overrides must say `override`.
- **`noImplicitReturns`** — all branches return when any branch does.
- **`noFallthroughCasesInSwitch`** — no silent fallthrough.
- **`noUnusedLocals` / `noUnusedParameters`** — agent leftovers get caught at type-check. For unused parameters in interface implementations, prefix with `_` instead of disabling the flag globally.
- **`allowUnreachableCode: false`** / **`allowUnusedLabels: false`** — treat as errors, not warnings.
When the toolchain supports it, also add `verbatimModuleSyntax` (any non-tsc transpiler), `isolatedDeclarations` (TS 5.5+ library), `erasableSyntaxOnly` (TS 5.8+ type-strip runtime).
## Agent Inner Loop
Add to `package.json` so the agent has a single command for the type-check feedback loop:
```json
{
"scripts": {
"typecheck": "tsc --noEmit",
"typecheck:watch": "tsc --noEmit --watch"
}
}
```
With `incremental: true` set, the second-and-later `npm run typecheck` returns in well under a second on typical projects — which is what makes this usable as an inner loop the agent runs between edits. When `typecheck` already targets another tool, leave it untouched and add `typecheck:tsc` alongside.
## When a Setting "Isn't Working", Show the Resolved Config
```bash
npx tsc --showConfig
```
Prints the fully resolved config after all `extends`. The single most useful command when a setting "isn't working" — run it before guessing.
## Merge Rules for an Existing tsconfig
When `tsconfig.json` already exists, do not overwrite. Surface the diff against the matching template above, flag any standing-policy violations (`strict: false`, `skipLibCheck: false`, trio mismatch), and let the user decide whether to migrate. Recommended migration order to minimize churn:
1. `strict: true`
2. `noUnusedLocals` + `noUnusedParameters`
3. `noImplicitReturns` + `noFallthroughCasesInSwitch`
4. `noUncheckedIndexedAccess`
5. `exactOptionalPropertyTypes`
6. `verbatimModuleSyntax`
Run `npm run typecheck` after each step. Commit between steps.
## Verification
- `npx tsc --noEmit` exits 0 on the project after the chosen template is applied (or surfaces real type errors that should be fixed).
- `npx tsc --showConfig` prints a resolved config matching the standing policy.
## References
- TypeScript compiler options: <https://www.typescriptlang.org/tsconfig>
- TypeScript modules guide: <https://www.typescriptlang.org/docs/handbook/modules/introduction.html>
More from kongyo2/agent-primary-ts-starters
- ts-npm-oxlint-starterAdd Oxlint to a TypeScript npm project with agent-friendly defaults that focus error-level diagnostics on real bugs, silence stylistic noise, and emit output in oxlint's `agent` format for downstream LLM consumption. Use when bootstrapping a new TS project that needs linting, or when adding Oxlint to an existing TS npm project — even if the user doesn't explicitly say "oxlint" or "linter".
- ts-npm-prettier-starterAdd Prettier to a TypeScript npm project with agent-friendly defaults that minimize diff size and keep line numbers stable across LLM-driven edits. Use when bootstrapping a new TS project that needs auto-formatting, or when adding Prettier to an existing TS npm project — even if the user doesn't explicitly say "prettier" or "formatter".
- ts-npm-zod-starterAdd zod to a TypeScript npm project and install the boundary-only parsing convention. Use when starting a new TS project that needs runtime schema validation, or when layering zod onto an existing TS npm project — even if the user doesn't explicitly say "zod" or "validation".