typescript-v6
TypeScript 6 Skill
Build, configure, and debug TypeScript 6+ projects with precise compiler guidance and modern module/runtime patterns.
Before You Start
This skill is for real TypeScript 6+ project work: daily development, configuration, debugging, and upgrades.
| Metric | Without Skill | With Skill |
|---|---|---|
| Upgrade Investigation Time | ~90 min | ~30 min |
| Common tsconfig Regressions | 5+ | 0-1 |
| Token Usage | High (manual diffing) | Low (release-note-grounded guidance) |
Known Issues This Skill Prevents
- Surprise build failures from missing
typesentries after upgrading - Unexpected
dist/src/...output becauserootDirwas never explicit - Deprecated
moduleResolution nodeorbaseUrlsettings surviving into a TS 6 migration - Confusion about when to use
bundlervsnodenext - Overusing
ignoreDeprecations: "6.0"as a long-term fix instead of a temporary migration aid - Misunderstanding
--stableTypeOrderingas a production performance flag instead of a TS 6→7 comparison tool - Missing Node/test globals because TS 6+ projects often need explicit
typesentries - New side-effect import errors because TS 6 applies stricter side-effect import checking
Quick Start
Step 1: Make the important options explicit
{
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist",
"types": ["node"],
"strict": true
},
"include": ["src/**/*"]
}
Why this matters: TypeScript 6 changed enough defaults and behaviors that explicit configuration now matters more in everyday work. rootDir and types are two of the most important settings to keep intentional.
Step 2: Pick module resolution deliberately
{
"compilerOptions": {
"module": "esnext",
"moduleResolution": "bundler"
}
}
Why this matters: TypeScript 6 deprecates moduleResolution: "node"/"node10". Bundled apps should usually choose bundler, while Node.js packages should usually choose nodenext.
Step 3: Use TS 6-era library typings only when the target/lib/runtime really supports them
const escaped = RegExp.escape('(hello)');
const value = new Map<string, number>().getOrInsert('count', 0);
const tomorrow = Temporal.Now.instant().add({ hours: 24 });
Why this matters: TypeScript 6 can type new platform APIs before every runtime ships them. Distinguish compiler types available from runtime support available.
Step 4: Verify config and resolution before changing code
npx tsc --noEmit
npx tsc --showConfig
npx tsc --explainFiles
Why this matters: TS 6+ projects often fail because the effective config or included file graph is not what the project expects. Validate that first, then refactor.
Critical Rules
Always Do
- Make
rootDirexplicit when your sources are nested below thetsconfig.json - Make the
typesarray explicit for Node, test runners, Workers, Bun, or other global type providers when the project relies on those ambient globals - Prefer
moduleResolution: "bundler"for bundled web apps andmoduleResolution: "nodenext"for modern Node.js packages - Treat
ignoreDeprecations: "6.0"as a short-term migration escape hatch, not the destination - Use
pathsdirectly instead of relying on deprecatedbaseUrl - Make
typesexplicit when the project truly depends on Node, test, Worker, or Bun globals - Treat side-effect imports as intentionally checked and fix their paths deliberately
- Verify runtime support before recommending
Temporal,getOrInsert, orRegExp.escape - Use
--stableTypeOrderingonly when comparing TS 6 and TS 7 behavior or investigating ordering-sensitive issues - Use
satisfies, exhaustiveneverchecks, and assertion functions when TS 6+ code exposes type ambiguity that should be made explicit - Re-run
tsc --noEmitafter config changes and again after type-pattern refactors
Never Do
- Never recommend deprecated
moduleResolution: "node"/"node10"as the forward-looking path - Never recommend removed
moduleResolution: "classic"as a fallback path - Never leave
typesimplicit if a project depends on@types/node, test globals, or platform globals - Never assume
ignoreDeprecations: "6.0"will keep working in TypeScript 7 - Never present TS 7 preview context as if it were already the default compiler runtime
- Never imply that TypeScript types guarantee runtime availability for new ECMAScript APIs
- Never import pre-TS 6 tsconfig advice that still uses
skipDefaultLibCheck,downlevelIteration, or old AMD/UMD/SystemJS examples
Common Mistakes
Wrong - relying on pre-TS 6 ambient type loading:
{
"compilerOptions": {
"outDir": "./dist"
}
}
Correct - declare what global types the project actually needs:
{
"compilerOptions": {
"outDir": "./dist",
"types": ["node"]
}
}
Why: In TS 6+, explicit types improves performance and predictability when the project depends on ambient globals.
Wrong - keep deprecated path alias setup unchanged:
{
"compilerOptions": {
"baseUrl": "./src",
"paths": {
"@app/*": ["app/*"]
}
}
}
Correct - inline the source prefix in paths:
{
"compilerOptions": {
"paths": {
"@app/*": ["./src/app/*"]
}
}
}
Why: baseUrl is deprecated in TS 6. The forward-looking setup is direct paths entries.
Wrong - type widening hides the real config contract:
const compilerMode = {
moduleResolution: 'bundler',
strict: true,
};
Correct - keep literals checked without widening:
const compilerMode = {
moduleResolution: 'bundler',
strict: true,
} satisfies {
moduleResolution: 'bundler' | 'nodenext';
strict: boolean;
};
Why: satisfies is not new in TS 6, but it is one of the cleanest ways to make config and option objects precise without losing inference.
Wrong - union handling silently misses a new case:
type ResolutionMode = 'bundler' | 'nodenext' | 'preserve';
function describeMode(mode: ResolutionMode) {
if (mode === 'bundler') return 'bundled app';
return 'node-style runtime';
}
Correct - exhaustive union handling:
type ResolutionMode = 'bundler' | 'nodenext' | 'preserve';
function describeMode(mode: ResolutionMode) {
switch (mode) {
case 'bundler':
return 'bundled app';
case 'nodenext':
return 'node-style runtime';
case 'preserve':
return 'mixed emit strategy';
default: {
const exhaustive: never = mode;
return exhaustive;
}
}
}
Why: TypeScript 6+ projects often rely on unions for config, platform, and runtime state. Exhaustive never checks make missing cases obvious.
Wrong - use stableTypeOrdering as a normal build flag:
tsc --stableTypeOrdering --build
Correct - use it only for comparison/debugging:
tsc --noEmit --stableTypeOrdering
Why: The flag exists to reduce TS 6 vs TS 7 output noise. It can meaningfully slow type-checking and is not intended as a permanent default.
Known Issues Prevention
| Issue | Root Cause | Solution |
|---|---|---|
process / describe / fs suddenly missing |
The project relied on ambient type discovery that is no longer safe to assume during TS 6 migration work | Add explicit entries like "types": ["node", "jest"] |
Output moves to dist/src/... |
The project relied on inferred source-root behavior that TS 6 migration work often needs to replace with explicit config | Set rootDir explicitly, usually ./src |
| Upgrade warnings explode | Deprecated module resolution or emit-era options survived from older configs | Migrate to bundler or nodenext; remove deprecated options |
| Side-effect imports suddenly error | Side-effect import checking is stricter in TS 6+ projects | Fix typos, add explicit files, or tighten import paths intentionally |
#/ imports do not resolve |
Runtime or resolution mode does not match TS 6 support requirements | Use Node 20+ support with moduleResolution: "bundler" or "nodenext" |
| New ES APIs compile but fail at runtime | TS lib types are present, runtime support is not | Verify runtime compatibility and polyfill strategy separately |
| Type ordering changes create noisy diffs | TS 6/TS 7 ordering differs during migration experiments | Use --stableTypeOrdering temporarily |
Bundled Resources
References
- Dedicated TS 6 migration guide →
references/migration-v6-reference.md - Defaults and configuration behavior →
references/defaults-migration-reference.md - Deprecations and replacements →
references/deprecations-reference.md - Module resolution and
#/imports →references/module-resolution-imports-reference.md - New library types and APIs →
references/stdlib-types-reference.md - Verification workflow and diagnostics →
references/workflow-diagnostics-reference.md - Type-safe patterns for TS 6+ code →
references/type-patterns-reference.md stableTypeOrderingand TS 7 context →references/stable-ordering-ts7-reference.md- Reference index →
references/README.md
Configuration Reference
Bundled application baseline
{
"compilerOptions": {
"target": "es2025",
"module": "esnext",
"moduleResolution": "bundler",
"lib": ["es2025", "dom"],
"rootDir": "./src",
"outDir": "./dist",
"strict": true,
"noUncheckedSideEffectImports": true
},
"include": ["src/**/*"]
}
Key settings:
rootDir: Prevents accidentaldist/src/...nestingtypes: Add it explicitly only when the project actually depends on Node/test/platform globalsmoduleResolution: "bundler": Best fit for Vite/esbuild/Rollup/Webpack-style app buildstarget: "es2025"/lib: ["es2025", ...]: Gives access to TS 6-era built-in types such asRegExp.escape
Node package baseline
{
"compilerOptions": {
"target": "es2022",
"module": "nodenext",
"moduleResolution": "nodenext",
"lib": ["es2022"],
"rootDir": "./src",
"outDir": "./dist",
"types": ["node"],
"strict": true
},
"include": ["src/**/*"]
}
Key settings:
nodenext: Use when the package's runtime semantics should follow modern Node.js ESM/CJS rules- Explicit
.jsimport specifiers andpackage.jsonmodule settings still matter; TS 6 does not remove that responsibility
Project Structure
my-ts-project/
├── src/
├── dist/
├── package.json
└── tsconfig.json
Why this matters: TS 6 rewards explicit, boring project structure. Most upgrade pain comes from old implicit config behavior, not from source code syntax.
Common Patterns
Direct paths migration
{
"compilerOptions": {
"paths": {
"@/*": ["./src/*"]
}
}
}
Use this instead of keeping deprecated baseUrl around.
#/ subpath imports for package-internal aliases
{
"name": "my-package",
"type": "module",
"imports": {
"#/*": "./dist/*"
}
}
import * as utils from '#/utils.js';
Use this when your package/runtime already supports Node's imports field and you want package-native aliases instead of bundler-only conventions. Keep the exact mapping aligned with the files the package actually ships.
Temporary migration shield
{
"compilerOptions": {
"ignoreDeprecations": "6.0"
}
}
Use this only long enough to unblock the migration. Plan to remove it before TS 7.
Monorepo/project-reference check
{
"compilerOptions": {
"composite": true,
"declaration": true,
"isolatedDeclarations": true,
"rootDir": "./src",
"outDir": "./dist"
}
}
Use this when packages emit .d.ts files or participate in project references. isolatedDeclarations is not TS 6-exclusive, but it fits the stricter, more explicit TS 6+ workflow well.
Verification Workflow
npx tsc --noEmit
npx tsc --showConfig
npx tsc --explainFiles
npx tsc --traceResolution
When to use each command:
--noEmit: First-pass health check after config or type changes--showConfig: Confirm the effective merged config before debugging phantom settings--explainFiles: Understand why a file is in the program or why a file graph changed--traceResolution: Debugpaths, package exports,types, or#/import resolution
Troubleshooting
"Cannot find name 'process'" / "Cannot find name 'describe'"
Add the appropriate types entries and install the matching @types/* package if needed.
Output path changed unexpectedly
Set rootDir explicitly. This is one of the most common TS 6 upgrade regressions.
Deprecated option warnings keep appearing
Migrate away from deprecated settings; use ignoreDeprecations: "6.0" only while the real replacement work is still in progress.
New side-effect import errors appear in TS 6+
Inspect the import path and whether the file is intended as a side-effect-only module. TS 6 applies stricter checking here, so old typos or vague side-effect imports can surface now.
RegExp.escape / Temporal / getOrInsert compile but fail in production
Check runtime support. TS 6 can expose types before every target environment implements the runtime API.
Setup Checklist
-
rootDiris explicit if source files live below thetsconfig.json -
typesis explicit for Node, tests, Workers, Bun, or other ambient platforms -
moduleResolutionisbundlerornodenext, not deprecatednode/node10 - Removed
classicresolution,skipDefaultLibCheck, anddownlevelIterationare not lingering in copied config - Deprecated
baseUrl/downlevelIteration/ ES5-era settings are removed or scheduled for removal -
tsc --showConfigandtsc --explainFileswere used if the upgrade behavior is still surprising -
ignoreDeprecations: "6.0"is temporary and tracked - New TS 6 APIs are validated against actual runtime support
-
--stableTypeOrderingis only used for migration comparisons, not normal builds