0Pricing
TypeScript Academy · Lesson

Avoiding Expensive Type Operations

Identify and fix deeply recursive or distributive types.

Avoiding Expensive Type Operations is a free TypeScript Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

What Makes a Type Operation Expensive?

Deeply recursive types, large union distributions, and complex infer chains force TypeScript to instantiate many type variants, causing exponential slowdowns.

// Expensive: distributes over every member of a large union
type FilterStrings<T> = T extends string ? T : never;
type Result = FilterStrings<string | number | boolean | null | undefined | ...>;

Avoid Excessive Union Sizes

Unions with hundreds of members (e.g., from many string literals) can make type checking very slow. Consider narrowing the domain or using string with validation.

// Expensive
type HugeUnion = "a" | "b" | "c" | /* 200 more ... */ "z";
// Better: string with a runtime check
function isValid(s: string): s is ValidString { return VALID_SET.has(s); }

Prefer Interfaces Over Complex Type Aliases

Interfaces are cached more aggressively by TypeScript than complex type alias intersections. Prefer interface for object shapes that are referenced many times.

// Slow: recomputed union intersection each time
type BigObject = TypeA & TypeB & TypeC & TypeD;
// Fast: interface (cached)
interface BigObject extends TypeA, TypeB, TypeC, TypeD {}

Limit Recursion Depth

Recursive conditional types are powerful but can hit TypeScript's depth limit (typically 100 levels). Add a depth counter to bail out early.

type Flatten<T, Depth extends number[] = []> =
  Depth["length"] extends 10 ? T
  : T extends (infer U)[] ? Flatten<U, [...Depth, 0]>
  : T;

Cache Intermediate Types

Name complex intermediate types so TypeScript can cache and reuse them instead of recomputing at every reference.

// Before: recomputed at each use
type MyResult<T> = T extends SomeComplex<infer U> ? Transform<U> : never;

// After: split into named intermediates
type ExtractU<T> = T extends SomeComplex<infer U> ? U : never;
type MyResult<T> = Transform<ExtractU<T>>;

Avoid Deep Mapped Types on Large Objects

Applying DeepReadonly to a very large object type causes TypeScript to traverse every property recursively, which is expensive.

// Expensive on 50-property nested objects
type Safe = DeepReadonly<HugeConfig>;
// Better: annotate at creation time with const assertions

Use Lazy Generic Evaluation

Wrapping an expensive type in a thunk (a function type that returns it) defers evaluation until the type is actually needed.

// Lazy evaluation via wrapper
type Lazy<T> = () => T;
type ExpensiveLazy = Lazy<DeepReadonly<HugeConfig>>;

Prefer Explicit Return Types

Explicitly annotating function return types prevents TypeScript from inferring them repeatedly at each call site, speeding up type checking.

// Let TypeScript infer (may be slow for complex functions)
function process(data: Input) { return transform(data); }

// Faster: explicit annotation
function process(data: Input): Output { return transform(data); }

isolatedDeclarations for Parallel Checking

TypeScript 5.5+ isolatedDeclarations requires explicit return types, enabling parallel type checking of independent files without full inference.

// tsconfig.json
{
  "compilerOptions": {
    "isolatedDeclarations": true
  }
}

Project References for Isolation

Project references prevent TypeScript from type-checking unchanged packages, which is the biggest win for large monorepos.

# With project references:
# tsc --build only recompiles packages whose sources changed
# Unchanged packages: declaration files used directly

Recap: Avoiding Expensive Types

To keep TypeScript fast: avoid huge unions, cache intermediate types, prefer interfaces over complex aliases, limit recursion depth, use explicit return types, and isolate packages with project references.

Quick Check

Which approach helps TypeScript cache an object shape more aggressively?

What You Learned

Expensive TypeScript type operations include large unions, deep recursion, and repeated complex inference. Optimize by caching intermediate types, preferring interfaces, limiting union sizes, and using project references for monorepos.

Frequently asked questions

Is the “Avoiding Expensive Type Operations” lesson free?

Yes — the full text of “Avoiding Expensive Type Operations” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.

What will I learn in “Avoiding Expensive Type Operations”?

Identify and fix deeply recursive or distributive types. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start TypeScript Academy?

No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Avoiding Expensive Type Operations” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this TypeScript Academy lesson?

Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Profiling Slow TypeScript Compilation
  2. Avoiding Expensive Type Operations
  3. skipLibCheck and Isolated Declarations
  4. Type-Checking in CI: Strategies and Tools
← Back to TypeScript Academy