git clone https://github.com/Intense-Visions/harness-engineering
T=$(mktemp -d) && git clone --depth=1 https://github.com/Intense-Visions/harness-engineering "$T" && mkdir -p ~/.claude/skills && cp -r "$T/agents/skills/codex/ts-performance-patterns" ~/.claude/skills/intense-visions-harness-engineering-ts-performance-patterns-842762 && rm -rf "$T"
agents/skills/codex/ts-performance-patterns/SKILL.mdTypeScript Performance Patterns
Reduce TypeScript compilation time and type complexity with targeted optimizations
When to Use
- TypeScript compilation or IDE type-checking is noticeably slow
- Complex generic types cause "type instantiation is excessively deep" errors
- Large monorepos need faster incremental builds
- IDE autocompletion or hover information is sluggish
Instructions
- Enable incremental compilation:
{ "compilerOptions": { "incremental": true, "tsBuildInfoFile": ".tsbuildinfo" } }
- Enable
to skip type-checkingskipLibCheck
files:.d.ts
{ "compilerOptions": { "skipLibCheck": true } }
This is the single biggest compilation speed improvement for most projects.
- Use project references to parallelize and cache builds:
{ "references": [{ "path": "packages/a" }, { "path": "packages/b" }] }
Build with
tsc --build for dependency-aware parallel compilation.
- Simplify deep generic types — break deeply nested generics into named intermediate types:
// Slow: deeply nested inline generics type Bad = Map<string, Array<Promise<Result<Map<string, Set<number>>>>>>; // Fast: named intermediates type NumberSets = Map<string, Set<number>>; type ResultPromises = Array<Promise<Result<NumberSets>>>; type Good = Map<string, ResultPromises>;
- Avoid recursive types that expand exponentially:
// Can cause "type instantiation excessively deep" errors type DeepPartial<T> = { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K]> : T[K]; }; // Add a depth limiter type DeepPartial<T, D extends number = 5> = D extends 0 ? T : { [K in keyof T]?: T[K] extends object ? DeepPartial<T[K], Prev[D]> : T[K] }; type Prev = [never, 0, 1, 2, 3, 4, 5];
- Use interfaces over type aliases for object types — interfaces are more efficiently cached:
// Faster — interface declaration is cached by name interface User { id: string; name: string; email: string; } // Slower — type alias is re-evaluated each time it's referenced type User = { id: string; name: string; email: string };
- Limit union type size — unions with >25 members can slow down type checking significantly. Group related types:
// Slow: 50-member union type Event = EventA | EventB | EventC | ... | EventAX; // Faster: grouped unions type UserEvent = UserCreated | UserUpdated | UserDeleted; type OrderEvent = OrderPlaced | OrderShipped | OrderCancelled; type Event = UserEvent | OrderEvent;
- Use
for fast single-file transpilation with esbuild or swc:isolatedModules
{ "compilerOptions": { "isolatedModules": true } }
- Exclude unnecessary files from compilation:
{ "include": ["src"], "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.stories.ts"] }
- Profile the compiler to find bottlenecks:
tsc --generateTrace ./trace # Open chrome://tracing and load the trace JSON
Details
TypeScript's type-checker is a constraint solver that runs at compile time. Complex types (deep generics, large unions, recursive mapped types) can cause exponential computation.
Common performance killers:
- Recursive conditional types without depth limits
- Large template literal type Cartesian products
- Hundreds of overloaded function signatures
- Deeply nested
chainsinfer
on incorrect code (the checker still processes it)@ts-ignore
Interface vs type alias performance: Interfaces create named cached entries in the type checker. When the same interface is referenced multiple times, the checker reuses the cached result. Type aliases with intersection types (
A & B & C) must be re-flattened at each use site.
justification: Third-party skipLibCheck
.d.ts files rarely change between builds, and their type errors are not actionable by your team. Skipping them saves 20-40% of compilation time on typical projects.
Project references trade-offs:
- Pros: parallel builds, cached compilation, explicit dependency graph
- Cons: more config files,
requirement, declaration files must be generatedcomposite: true - Best for: monorepos with 3+ packages. Overkill for single-package projects
Measuring improvement: Run
tsc --noEmit --diagnostics to see check time, program size, and memory usage. Compare before and after optimizations.
Source
https://typescriptlang.org/docs/handbook/performance.html
Process
- Read the instructions and examples in this document.
- Apply the patterns to your implementation, adapting to your specific context.
- Verify your implementation against the details and edge cases listed above.
Harness Integration
- Type: knowledge — this skill is a reference document, not a procedural workflow.
- No tools or state — consumed as context by other skills and agents.
Success Criteria
- The patterns described in this document are applied correctly in the implementation.
- Edge cases and anti-patterns listed in this document are avoided.