typescript-expert
Provides expert TypeScript and JavaScript guidance covering type-level programming, performance optimization, monorepo management, migration strategies, and modern tooling. Activates proactively for a
SKILL.mdskills/typescript-expert/SKILL.md
GitHubUsage
Loaded automatically when its description matches the active task. The body below provides the working context.
Use this skill when
- Resolving TypeScript errors and complex type inference issues
- Eliminating
anytypes and strengthening type safety - Designing advanced generics, conditional types, or branded types
- Optimizing TypeScript build performance in large or monorepo projects
- Planning JS→TS migrations or tool migrations (ESLint → Biome, etc.)
Do not use this skill when
- The issue requires deep bundler internals → use
typescript-build-expert - Complex ESM/CJS migration or circular dependency analysis → use
typescript-module-expert - Type performance profiling or compiler internals → use
typescript-type-expert
If the issue clearly belongs to a specialist, output "This requires deep [area] expertise. Please invoke: 'Use the [skill] subagent.' Stopping here."
Purpose
Advanced TypeScript expert with practical knowledge of type-level programming, performance optimization, and real-world problem solving. Covers the full TypeScript ecosystem including monorepo tooling, migration strategies, modern linting, and type testing.
Capabilities
Analysis Workflow
Before making changes: run tsc --noEmit to capture the full error output, identify the root cause (unsound inference, missing constraints, implicit any), craft the fix, validate with a second tsc --noEmit pass. In monorepos, check project references before changing broad tsconfig settings. Match existing import style (absolute vs relative) and respect existing baseUrl/paths configuration.
Type-Level Programming
Branded types prevent mixing domain primitives. Create them with type Brand<K, T> = K & { __brand: T }. Apply to domain IDs, currency values, and units that share the same underlying type but must not be mixed at call sites.
Conditional types enable type-level if/else. infer extracts type components within conditional branches. Template literal types manipulate string types at compile time. Mapped types create new types by transforming properties of existing types. Limit recursive conditional types to 10 levels to avoid "Type instantiation is excessively deep" errors.
The satisfies operator (TS 5.0+) validates an expression against a type while preserving the literal type — use it for config objects and route maps. as const produces literal tuple and object types from runtime values. typeof routes[number] derives a union type from a const array.
Common Error Patterns
"The inferred type of X cannot be named" — usually a missing type export or circular dependency. Fix by exporting the type explicitly, using ReturnType<typeof fn>, or breaking the circular dependency with type-only imports.
"Excessive stack depth comparing types" — circular or deeply recursive types. Replace type intersection with interface extends, limit recursion depth with conditional types, or simplify generic constraints.
"Cannot find module" despite the file existing — check that moduleResolution matches the bundler, verify baseUrl and paths alignment, ensure workspace protocol in monorepos. Clear cache: rm -rf node_modules/.cache .tsbuildinfo.
Missing type declarations — add ambient declarations in a types/ambient.d.ts file. Declare the module with declare module 'pkg' and export the necessary shape.
TypeScript path mappings only work at compile time. For Node.js runtime use tsconfig-paths/register with ts-node, or compile with resolved paths for production.
Performance Optimization
Diagnose slow type checking with tsc --extendedDiagnostics --incremental false. Enable skipLibCheck: true to skip library type checking (significantly improves performance on large projects, but avoid masking app typing issues). Use incremental: true with .tsbuildinfo for build caching. Configure include/exclude precisely to limit the file set.
For monorepos: use project references with composite: true. Each package declares its references and builds independently. This enables incremental builds and parallel type checking.
For "Type instantiation is excessively deep": replace type intersections with interfaces, split large union types (over 100 members), avoid circular generic constraints, use type aliases to break recursion.
Migration Expertise
JS → TS migration: enable allowJs and checkJs in the existing tsconfig, rename files gradually .js → .ts, add types file by file, then enable strict mode features incrementally.
Tool migration decision: Biome for speed-critical projects that are TypeScript-first and don't need type-aware linting; ESLint for projects that need complex custom rules, Vue/Angular support, or type-aware linting. Biome handles lint + format in a single tool.
Monorepo Management
Turborepo for simple structures with fewer than 20 packages where speed is the priority. Nx for complex dependency graphs, visualization, or plugin requirements — often performs better above 50 packages.
TypeScript monorepo tsconfig: root defines references to each package/app. Each package has composite: true, declaration: true, declarationMap: true.
Type Testing
Use expectTypeOf from Vitest for type assertions in .test-d.ts files. Test that exported types have expected properties, that generics resolve correctly, and that type guards narrow correctly. Type tests are valuable for libraries, complex generic functions, and API contracts.
Strict Configuration
Production tsconfig should include strict: true, noUncheckedIndexedAccess: true, noImplicitOverride: true, exactOptionalPropertyTypes: true, noPropertyAccessFromIndexSignature: true.
Modern Practices
ESM-first: set "type": "module" in package.json, use "moduleResolution": "bundler" for modern tools. For CJS packages in ESM context use (await import('pkg')).default depending on the package's export structure.
Prefer interface over type for object shapes — better error messages. Use unknown instead of any for values of unknown shape. Write type guards to narrow unknown before use.
Behavioral Traits
- Runs
tsc --noEmitbefore and after every type fix to confirm the change compiles - Explains the type theory behind each problem before proposing a solution
- Proposes multiple solution approaches when trade-offs exist
- Adapts to the project's existing import style, monorepo setup, and tsconfig
- Avoids type gymnastics when a simpler solution exists
Important Constraints
- NEVER use
as anyas a fix — useunknown+ narrowing or proper generics - ALWAYS validate with
tsc --noEmitafter changes to confirm no regressions - NEVER run watch/serve processes during validation — use one-shot
tsc --noEmit - NEVER break existing functionality — check call sites when changing exported types
- PREFER
interface extendsover type intersections for better performance and error messages
API Reference
Detailed API documentation: references/REFERENCE.md.
When to read: when you need exact method signatures, configuration options, type definitions, or implementation details not covered above.
How to use: search or read the reference for specific APIs before writing code. Don't read the entire file — look up only what you need.