src/skills/shared-tooling-typescript-config/SKILL.md
TypeScript strict mode configs, TS 5.x+ options (verbatimModuleSyntax, module preserve, moduleDetection force, configDir), path alias sync, specialized configs
npx skillsauth add agents-inc/skills shared-tooling-typescript-configInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Quick Guide: Shared TypeScript strict config in
packages/typescript-config/. Enablestrict: trueplusnoUncheckedIndexedAccess,exactOptionalPropertyTypes,noImplicitOverride. Use modern module settings:module: "preserve",moduleResolution: "bundler",verbatimModuleSyntax: true,moduleDetection: "force". Use${configDir}(TS 5.5+) for portable paths. Sync path aliases between tsconfig and your build tool.
<critical_requirements>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST enable TypeScript strict mode (strict: true) in ALL tsconfig.json files - non-negotiable)
(You MUST use verbatimModuleSyntax: true to enforce explicit import type - replaces deprecated importsNotUsedAsValues)
(You MUST use shared config pattern (packages/typescript-config/) in monorepos - never duplicate configs per package)
(You MUST sync path aliases between tsconfig.json and your build tool - mismatches cause import resolution failures)
(You MUST use modern module settings: module: "preserve", moduleResolution: "bundler" for bundler-based projects)
</critical_requirements>
Auto-detection: TypeScript config, tsconfig.json, tsconfig, strict mode, noUncheckedIndexedAccess, exactOptionalPropertyTypes, verbatimModuleSyntax, moduleDetection force, module preserve, moduleResolution bundler, configDir, path aliases, typescript-config, shared config, noImplicitOverride, isolatedDeclarations, erasableSyntaxOnly, import defer
When to use:
isolatedDeclarations, erasableSyntaxOnly, configDir, import defer)When NOT to use:
Key patterns covered:
${configDir} template variable for portable shared configs (TS 5.5+)isolatedDeclarations for parallel build support (TS 5.5+)erasableSyntaxOnly for Node.js direct execution (TS 5.8+)import defer for deferred module evaluation (TS 5.9+)Detailed resources:
TypeScript configuration should be strict by default, shared across packages, and forward-compatible. Every project starts with the strictest settings. Shared configs prevent drift. Modern module settings align with bundler-based workflows.
Core principles:
strict: true plus noUncheckedIndexedAccess, exactOptionalPropertyTypes, noImplicitOverridemodule: "preserve" + moduleResolution: "bundler" for bundler projects; "node18"/"node20" for Node.jsAll apps and packages extend a shared strict base. The base config lives in packages/typescript-config/ (monorepo) or is inlined in a standalone project.
// packages/typescript-config/base.json (abbreviated)
{
"compilerOptions": {
"strict": true,
"exactOptionalPropertyTypes": true,
"noUncheckedIndexedAccess": true,
"noImplicitOverride": true,
"module": "preserve",
"moduleResolution": "bundler",
"verbatimModuleSyntax": true
}
}
Consumer configs extend the shared base and only add what differs (e.g. paths).
Why good: Single source of truth for strict settings, all packages get the same safety guarantees, consumers only add what differs
See examples/core.md for full base config, consumer configs, and specialized configs (react.json, node.json, library.json).
Enforces explicit import type for type-only imports. Replaces deprecated importsNotUsedAsValues and preserveValueImports.
// With verbatimModuleSyntax: true
// Good - explicit type import
import type { User } from "./types";
import { createUser } from "./api";
// Bad - type imported as value (errors with verbatimModuleSyntax)
import { User, createUser } from "./api";
Preserves import/export syntax as-is. TypeScript only type-checks; the bundler handles module output.
When to use: Bundler-based projects where TypeScript does NOT emit JavaScript (noEmit: true)
When not to use: Node.js packages that emit CJS/ESM directly -- use module: "node18" or "node20" instead
Forces all files to be treated as modules, even without import/export. Prevents unexpected global scope pollution.
Makes shared configs portable. ${configDir} resolves to the directory of the leaf config (the one that extends), not the base.
// packages/typescript-config/base.json
{
"compilerOptions": {
"outDir": "${configDir}/dist",
"rootDir": "${configDir}/src"
},
"include": ["${configDir}/src"]
}
Why good: Consumers don't need to override outDir, rootDir, or include -- paths resolve relative to their own directory
Gotcha: Without ${configDir}, relative paths like "./dist" resolve from the base config's location (packages/typescript-config/), not the consuming package.
Path aliases must be configured in BOTH tsconfig.json AND your build tool. A mismatch causes either TypeScript errors or build-time import resolution failures.
// tsconfig.json -- paths here for TypeScript type checking
{ "compilerOptions": { "paths": { "@/*": ["./src/*"] } } }
// Build tool config -- SAME aliases for bundler resolution
// resolve: { alias: { "@": path.resolve(__dirname, "./src") } }
Gotcha: Forgetting to sync causes "module not found" errors. TypeScript resolves fine but the bundler fails (or vice versa). When adding a new alias, always update BOTH files.
See examples/core.md for full tsconfig + build tool config examples.
Requires explicit type annotations on all exports. Enables parallel .d.ts generation by external tools (oxc, swc).
// With isolatedDeclarations: true
// Good - explicit return type on export
export function getUser(id: string): User {
return { id, name: "John" };
}
// Bad - inferred return type (will error)
export function getUser(id: string) {
return { id, name: "John" };
}
When to use: Large monorepos publishing library packages where parallel .d.ts generation speeds up builds
When not to use: Application code that is never consumed as a library (adds verbosity for no benefit)
Prohibits TypeScript-specific constructs that have runtime behavior (enums, namespaces, parameter properties). Ensures compatibility with Node.js --experimental-strip-types for direct TS execution.
Blocked constructs: enum, const enum, runtime namespace, parameter properties (constructor(public name: string))
Allowed constructs: type, interface, type-only imports -- anything that can be erased without changing runtime behavior
When to use: Projects using Node.js direct TS execution, or targeting the types-only philosophy
When not to use: Projects that rely heavily on enums, namespaces, or parameter properties
See examples/core.md for full good/bad code examples.
Deferred module evaluation -- the module loads but doesn't execute until an export is accessed. Only works with namespace imports under --module preserve or esnext.
import * as analytics from "./heavy-analytics.js";
// Module is loaded but NOT executed yet
if (needsAnalytics) {
analytics.track("event"); // Module executes here on first access
}
When to use: Improving startup performance for conditionally-loaded heavy modules
Limitation: Named imports and default imports are not supported with import defer -- only namespace syntax (import defer * as ...)
TS 6.0 (February 2026) changes several defaults. If your config already follows the patterns above, most changes are transparent.
| Option | Old Default | TS 6.0 Default |
| ------------------------------ | ------------------------ | -------------------- |
| strict | false | true |
| module | commonjs | esnext |
| target | es3 | es2025 |
| rootDir | inferred | . (current dir) |
| types | auto-discover @types/* | [] (explicit only) |
| noUncheckedSideEffectImports | false | true |
| libReplacement | true | false |
Action required: Set "types": ["node"] (or relevant packages) explicitly after upgrading to TS 6.0 -- the auto-discovery of @types/* is gone.
Deprecated in TS 6.0 (removed in TS 7.0):
target: "es5", moduleResolution: "node" (node10), module: "amd"|"umd"|"systemjs"|"none"esModuleInterop: false, --baseUrl as module resolution root, --outFileUse "ignoreDeprecations": "6.0" during migration to suppress warnings.
See examples/core.md for full TS 6.0 defaults table and deprecation details.
Prevents TypeScript from inferring a type from a specific position in generic functions.
// Good - NoInfer ensures initial must be from states array
declare function createFSM<TState extends string>(config: {
initial: NoInfer<TState>;
states: TState[];
}): void;
createFSM({
initial: "invalid", // Error: "invalid" not in states
states: ["open", "closed"],
});
Why good: TypeScript infers TState from states only; initial must match without widening the union
When to use: Generic functions where one parameter should constrain another, not expand the inferred type
</patterns><decision_framework>
What module/moduleResolution to use?
├─ Bundler-based project (TypeScript does NOT emit JS)?
│ └─ YES -> module: "preserve", moduleResolution: "bundler"
├─ Node.js package (direct execution)?
│ ├─ Node 20+? -> module: "node20" (TS 5.9+, stable)
│ └─ Node 18+? -> module: "node18" (TS 5.8+)
└─ Library consumed by both bundlers and Node?
└─ module: "nodenext" (most compatible)
What target to use?
├─ Bundler-based project? -> target: "ES2022" (stable, well-supported)
├─ Node.js 18+? -> target: "ES2022"
├─ Node.js 20+? -> target: "ES2023"
└─ TS 6.0+ project? -> target: "es2025" (new default)
isolatedDeclarations?
├─ Publishing library packages in large monorepo? -> Enable
├─ Application code only? -> Skip (adds verbosity for no benefit)
└─ Using oxc or swc for builds? -> Enable (these tools benefit most)
erasableSyntaxOnly?
├─ Using Node.js --experimental-strip-types? -> Enable (required)
├─ Project uses enums extensively? -> Skip (migration cost too high)
└─ Standard bundler project? -> Optional (nice-to-have)
See reference.md for additional decision frameworks (shared vs local config, TS 6.0 migration).
</decision_framework>
<red_flags>
High Priority Issues:
strict: false) -- allows implicit any and null bugs across the projectverbatimModuleSyntax: true -- type imports may or may not be elided depending on transpilerimportsNotUsedAsValues or preserveValueImports -- replaced by verbatimModuleSyntax since TS 5.0Medium Priority Issues:
moduleResolution: "node" (node10) instead of "bundler" or "node18" -- deprecated in TS 6.0target: "ES3" or "ES5" -- deprecated in TS 6.0, removed in TS 7.0Gotchas & Edge Cases:
${configDir} resolves to the directory of the leaf config (the one that uses extends), not the base configverbatimModuleSyntax requires ALL type-only imports to use import type -- mixed imports like import { Type, value } error if Type is type-only. Use inline syntax: import { type Type, value }exactOptionalPropertyTypes means { key?: string } does NOT accept { key: undefined } -- only omission or stringnoUncheckedIndexedAccess adds | undefined to ALL index signatures, including arrays -- use for...of or guard with ifmodule: "preserve" only works with noEmit: true or emitDeclarationOnly: truetypes default to [] -- @types/node, @types/react etc. must be explicitly listed after upgradeimport defer only supports namespace syntax (import defer * as ...), not named or default imports</red_flags>
<critical_reminders>
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST enable TypeScript strict mode (strict: true) in ALL tsconfig.json files - non-negotiable)
(You MUST use verbatimModuleSyntax: true to enforce explicit import type - replaces deprecated importsNotUsedAsValues)
(You MUST use shared config pattern (packages/typescript-config/) in monorepos - never duplicate configs per package)
(You MUST sync path aliases between tsconfig.json and your build tool - mismatches cause import resolution failures)
(You MUST use modern module settings: module: "preserve", moduleResolution: "bundler" for bundler-based projects)
Failure to follow these rules will cause type-safety gaps, inconsistent configs, and import resolution failures.
</critical_reminders>
development
Material Design component library for Vue 3
development
VitePress 1.x — Vue-powered static site generator for documentation sites, built on Vite
tools
Docusaurus 3.x documentation framework — site configuration, docs/blog plugins, sidebars, versioning, MDX, swizzling, and deployment
development
TanStack Form patterns - useForm, form.Field, validators, arrays, linked fields, createFormHook, type safety