Performance

3 min read
Rapid overview

TypeScript Performance Optimization

Performance considerations specific to TypeScript development.

TypeScript Compiler Performance

Compilation Speed

Project References:

// tsconfig.json
{
  "references": [
    { "path": "./packages/core" },
    { "path": "./packages/ui" }
  ]
}

Incremental Compilation:

// tsconfig.json
{
  "compilerOptions": {
    "incremental": true,
    "tsBuildInfoFile": "./.tsbuildinfo"
  }
}

Skip Lib Check:

{
  "compilerOptions": {
    "skipLibCheck": true // Faster builds
  }
}

Type Performance

Avoid Complex Type Unions:

// ❌ Slow - large union
type Status = 'idle' | 'pending' | 'success' | 'error' | /* 100 more */;

// ✅ Fast - use enums or const assertions
const Status = {
  IDLE: 'idle',
  PENDING: 'pending',
  SUCCESS: 'success',
  ERROR: 'error'
} as const;

type StatusType = typeof Status[keyof typeof Status];

Prefer Interfaces Over Types for Objects:

// ✅ Faster for extending
interface User {
  name: string;
  age: number;
}

interface Employee extends User {
  role: string;
}

Runtime Performance

Type Assertions vs Type Guards

// Type assertion (no runtime cost)
const value = someValue as string;

// Type guard (runtime cost, but safer)
function isString(value: unknown): value is string {
  return typeof value === 'string';
}

if (isString(value)) {
  // TypeScript knows value is string
  console.log(value.toUpperCase());
}

Const Enums (Inlined)

// ❌ Regular enum (generates runtime code)
enum Direction {
  Up,
  Down,
  Left,
  Right
}

// ✅ Const enum (inlined at compile time, no runtime cost)
const enum Direction {
  Up,
  Down,
  Left,
  Right
}

// Compiled output uses literal values

Generic Type Constraints

// More specific constraints = better performance hints
function process<T extends { id: number }>(item: T): number {
  return item.id; // TypeScript knows id exists
}

Build Optimization

Tree Shaking

// ✅ Use ES modules for tree shaking
export const utilA = () => { /* ... */ };
export const utilB = () => { /* ... */ };

// Import only what you need
import { utilA } from './utils';

Barrel Files Consideration

// ❌ Can hurt tree shaking
// index.ts
export * from './moduleA';
export * from './moduleB';
export * from './moduleC';

// ✅ Better - direct imports
import { functionA } from './moduleA';

Type-Safe Performance Patterns

Memoization with Types

function memoize<T extends (...args: any[]) => any>(fn: T): T {
  const cache = new Map();

  return ((...args: Parameters<T>): ReturnType<T> => {
    const key = JSON.stringify(args);
    if (cache.has(key)) {
      return cache.get(key);
    }
    const result = fn(...args);
    cache.set(key, result);
    return result;
  }) as T;
}

const expensiveCalculation = memoize((n: number) => {
  // Heavy computation
  return n * 2;
});

Typed Lazy Loading

type LazyModule<T> = () => Promise<{ default: T }>;

async function loadComponent<T>(loader: LazyModule<T>): Promise<T> {
  const module = await loader();
  return module.default;
}

// Usage
const MyComponent = await loadComponent(() => import('./MyComponent'));

Best Practices

  1. Use strictNullChecks - Catches potential runtime errors at compile time
  2. Enable noImplicitAny - Prevents accidental type holes
  3. Use Project References - For large monorepos
  4. Leverage Incremental Compilation - Faster rebuilds
  5. Const Enums - When you don't need runtime enum objects
  6. Type-Only Imports - For better tree shaking
  7. Avoid Deep Type Recursion - Can slow down compilation
// Type-only imports (removed at runtime)
import type { User } from './types';

Measuring TypeScript Compilation

# Measure compilation time
tsc --diagnostics

# Extended diagnostics
tsc --extendedDiagnostics

# List files being compiled
tsc --listFiles

Performance Tips

  • Use const assertions for literal types
  • Prefer interface over type for object shapes
  • Use mapped types judiciously
  • Avoid excessive type generics
  • Use discriminated unions for better type narrowing
  • Enable skipLibCheck for faster builds
  • Use project references for large codebases