advanced-topics
TypeScript Advanced Topics
Section titled “TypeScript Advanced Topics”1. Advanced Type System Expertise
Section titled “1. Advanced Type System Expertise”Type-Level Programming Patterns
Section titled “Type-Level Programming Patterns”Branded Types for Domain Modeling
// Create nominal types to prevent primitive obsessiontype Brand<K, T> = K & { __brand: T };type UserId = Brand<string, 'UserId'>;type OrderId = Brand<string, 'OrderId'>;
// Prevents accidental mixing of domain primitivesfunction processOrder(orderId: OrderId, userId: UserId) { }- Use for: Critical domain primitives, API boundaries, currency/units
- Resource: https://egghead.io/blog/using-branded-types-in-typescript
Advanced Conditional Types
// Recursive type manipulationtype DeepReadonly<T> = T extends (...args: any[]) => any ? T : T extends object ? { readonly [K in keyof T]: DeepReadonly<T[K]> } : T;
// Template literal type magictype PropEventSource<Type> = { on<Key extends string & keyof Type> (eventName: `${Key}Changed`, callback: (newValue: Type[Key]) => void): void;};- Use for: Library APIs, type-safe event systems, compile-time validation
- Watch for: Type instantiation depth errors (limit recursion to 10 levels)
Type Inference Techniques
// Use 'satisfies' for constraint validation (TS 5.0+)const config = { api: "https://api.example.com", timeout: 5000} satisfies Record<string, string | number>;// Preserves literal types while ensuring constraints
// Const assertions for maximum inferenceconst routes = ['/home', '/about', '/contact'] as const;type Route = typeof routes[number]; // '/home' | '/about' | '/contact'Performance Optimization Strategies
Section titled “Performance Optimization Strategies”Type Checking Performance
# Diagnose slow type checkingnpx tsc --extendedDiagnostics --incremental false | grep -E "Check time|Files:|Lines:|Nodes:"
# Common fixes for "Type instantiation is excessively deep"# 1. Replace type intersections with interfaces# 2. Split large union types (>100 members)# 3. Avoid circular generic constraints# 4. Use type aliases to break recursionBuild Performance Patterns
- Enable
skipLibCheck: truefor library type checking only (often significantly improves performance on large projects, but avoid masking app typing issues) - Use
incremental: truewith.tsbuildinfocache - Configure
include/excludeprecisely - For monorepos: Use project references with
composite: true
2. Real-World Problem Resolution
Section titled “2. Real-World Problem Resolution”Complex Error Patterns
Section titled “Complex Error Patterns”“The inferred type of X cannot be named”
- Cause: Missing type export or circular dependency
- Fix priority:
- Export the required type explicitly
- Use
ReturnType<typeof function>helper - Break circular dependencies with type-only imports
- Resource: https://github.com/microsoft/TypeScript/issues/47663
Missing type declarations
- Quick fix with ambient declarations:
declare module 'some-untyped-package' { const value: unknown; export default value; export = value; // if CJS interop is needed}- For more details: Declaration Files Guide
“Excessive stack depth comparing types”
- Cause: Circular or deeply recursive types
- Fix priority:
- Limit recursion depth with conditional types
- Use
interfaceextends instead of type intersection - Simplify generic constraints
// Bad: Infinite recursiontype InfiniteArray<T> = T | InfiniteArray<T>[];
// Good: Limited recursiontype NestedArray<T, D extends number = 5> = D extends 0 ? T : T | NestedArray<T, [-1, 0, 1, 2, 3, 4][D]>[];Module Resolution Mysteries
- “Cannot find module” despite file existing:
- Check
moduleResolutionmatches your bundler - Verify
baseUrlandpathsalignment - For monorepos: Ensure workspace protocol (workspace:*)
- Try clearing cache:
rm -rf node_modules/.cache .tsbuildinfo
- Check
Path Mapping at Runtime
- TypeScript paths only work at compile time, not runtime
- Node.js runtime solutions:
- ts-node: Use
ts-node -r tsconfig-paths/register - Node ESM: Use loader alternatives or avoid TS paths at runtime
- Production: Pre-compile with resolved paths
- ts-node: Use
Migration Expertise
Section titled “Migration Expertise”JavaScript to TypeScript Migration
# Incremental migration strategy# 1. Enable allowJs and checkJs (merge into existing tsconfig.json):# Add to existing tsconfig.json:# {# "compilerOptions": {# "allowJs": true,# "checkJs": true# }# }
# 2. Rename files gradually (.js → .ts)# 3. Add types file by file using AI assistance# 4. Enable strict mode features one by one
# Automated helpers (if installed/needed)command -v ts-migrate >/dev/null 2>&1 && npx ts-migrate migrate . --sources 'src/**/*.js'command -v typesync >/dev/null 2>&1 && npx typesync # Install missing @types packagesTool Migration Decisions
| From | To | When | Migration Effort |
|---|---|---|---|
| ESLint + Prettier | Biome | Need much faster speed, okay with fewer rules | Low (1 day) |
| TSC for linting | Type-check only | Have 100+ files, need faster feedback | Medium (2-3 days) |
| Lerna | Nx/Turborepo | Need caching, parallel builds | High (1 week) |
| CJS | ESM | Node 18+, modern tooling | High (varies) |
Monorepo Management
Section titled “Monorepo Management”Nx vs Turborepo Decision Matrix
- Choose Turborepo if: Simple structure, need speed, <20 packages
- Choose Nx if: Complex dependencies, need visualization, plugins required
- Performance: Nx often performs better on large monorepos (>50 packages)
TypeScript Monorepo Configuration
// Root tsconfig.json{ "references": [ { "path": "./packages/core" }, { "path": "./packages/ui" }, { "path": "./apps/web" } ], "compilerOptions": { "composite": true, "declaration": true, "declarationMap": true }}3. Modern Tooling Expertise
Section titled “3. Modern Tooling Expertise”Biome vs ESLint
Section titled “Biome vs ESLint”Use Biome when:
- Speed is critical (often faster than traditional setups)
- Want single tool for lint + format
- TypeScript-first project
- Okay with 64 TS rules vs 100+ in typescript-eslint
Stay with ESLint when:
- Need specific rules/plugins
- Have complex custom rules
- Working with Vue/Angular (limited Biome support)
- Need type-aware linting (Biome doesn’t have this yet)
Type Testing Strategies
Section titled “Type Testing Strategies”Vitest Type Testing (Recommended)
// in avatar.test-d.tsimport { expectTypeOf } from 'vitest'import type { Avatar } from './avatar'
test('Avatar props are correctly typed', () => { expectTypeOf<Avatar>().toHaveProperty('size') expectTypeOf<Avatar['size']>().toEqualTypeOf<'sm' | 'md' | 'lg'>()})When to Test Types:
- Publishing libraries
- Complex generic functions
- Type-level utilities
- API contracts
4. Debugging Mastery
Section titled “4. Debugging Mastery”CLI Debugging Tools
Section titled “CLI Debugging Tools”# Debug TypeScript files directly (if tools installed)command -v tsx >/dev/null 2>&1 && npx tsx --inspect src/file.tscommand -v ts-node >/dev/null 2>&1 && npx ts-node --inspect-brk src/file.ts
# Trace module resolution issuesnpx tsc --traceResolution > resolution.log 2>&1grep "Module resolution" resolution.log
# Debug type checking performance (use --incremental false for clean trace)npx tsc --generateTrace trace --incremental false# Analyze trace (if installed)command -v @typescript/analyze-trace >/dev/null 2>&1 && npx @typescript/analyze-trace trace
# Memory usage analysisnode --max-old-space-size=8192 node_modules/typescript/lib/tsc.jsCustom Error Classes
Section titled “Custom Error Classes”// Proper error class with stack preservationclass DomainError extends Error { constructor( message: string, public code: string, public statusCode: number ) { super(message); this.name = 'DomainError'; Error.captureStackTrace(this, this.constructor); }}