Skip to content

Instantly share code, notes, and snippets.

@ssalbdivad
Created August 31, 2025 13:20
Show Gist options
  • Save ssalbdivad/ec7628cfc36354a5d20b86390586593e to your computer and use it in GitHub Desktop.
Save ssalbdivad/ec7628cfc36354a5d20b86390586593e to your computer and use it in GitHub Desktop.

TypeScript Type Performance Cheat Sheet

Principles

1. Build up, not down

Combine sets of properties using interface extensions rather than extracting them using utilities like Omit.

❌

interface AllProps {
    foo: string
    bar: boolean
    baz: number
}

type BarVariant = Omit<AllProps, "baz">
type BazVariant = Omit<AllProps, "bar">

βœ…

interface BaseProps {
    foo: string
}

interface WithBar extends BaseProps {
    bar: boolean
}

interface WithBaz extends BaseProps {
    baz: number
}

2. Think of generics like memoized functions

This has lots of implications when designing efficient types, including principle 3.

One that makes your life easier is you don't have to worry about duplicate instantiations.

πŸ†—

import { doExpensiveThing } from "./complexTypes.ts"

type getResult<t> =
    // the second doExpensiveThing<T> will not be evaluated
    // the repetition can safely be ignored from a perf perspective
    doExpensiveThing<t> extends SomeType ? doExpensiveThing<t> : t

3. Minimize type parameter footprint

In accordance with 2, generics type parameters should reflect the minimum information needed to determine the correct output.

Extra context that could be innocuous at runtime will lead to worse caching.

❌

type doExpensiveThing<ctx extends BigContextObject> =
    ctx["relevantKey"] extends SomeType
        ? // ...series of conditionals + transforms on Ctx["relevantKey"]
          never
        : never

type Result = doExpensiveThing<MyBigContextObject>

βœ…

type doExpensiveThing<relevantValue extends ExactlyWhatYouNeed> =
    relevantValue extends SomeType
        ? // ...series of conditionals + transforms on RelevantValue
          never
        : never

type Result = doExpensiveThing<MyBigContextObject["relevantKey"]>

4. Use fewer mapped types that do more

Rather than composing a series of builtins like Partial and Omit to do what you want, write a single type that will transform the type the way you need.

❌

// pick numeric keys, make keys optional, convert values to arrays
type transform<o extends object> = valuesToArrays<Pick<Partial<o>, `${number>}`>>

type valuesToArrays<o extends object> = { [k in keyof o]: o[k][] }

βœ…

// pick numeric keys, make keys optional, convert values to arrays
type transform<o extends object> = {
    [k in keyof o as k extends `${number}` ? k : never]?: o[k][]
}

5. Beware of large unions

If needed, ensure they are discriminated and avoid intersecting them with other unions

☠️

// inherently O(N^2)- find another way to get types that are "good enough"
type Result = LargeUnionA & LargeUnionB

// checking assignability can be similarly expensive if not discriminated
const result: LargeUnionA = getUnionB()

Tools

Instantiation Benchmarking

Type instantiations are a useful heuristic for performance and can granularly captured by @ark/attest:

import { bench } from "@ark/attest"

type makeComplexType<s extends string> = s extends `${infer head}${infer tail}`
    ? head | tail | makeComplexType<tail>
    : s

bench("makeComplexType", () => {
    return {} as makeComplexType<"defenestration">
    // this is an inline snapshot that will be populated or compared
    // when you run the file. it reflects the number of type instantiations
    // directly contributed by the body of the `bench` call, including
    // function calls, assignments and type instantiations like this one.
}).types([169, "instantiations"])

This can be extremely useful when optimizing the implementation of a particular type and for avoiding regressions in CI.

Docs: https://github.com/arktypeio/arktype/tree/main/ark/attest#benches

Tracing

TypeScript's --generateTrace option can capture type performance trace data for your project. This is a great way to get a top-down view of type perf for a project and build intuitions about where to start optimizing.

@ark/attest's CLI supplements this with some additional analysis around the most expensive function calls in your repo.

Docs: https://github.com/arktypeio/arktype/tree/main/ark/attest#trace

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment