Conditional + mapped types cheatsheet.

Conditional basics

type IsString<T> = T extends string ? true : false;

type A = IsString<"hi">;        // true
type B = IsString<42>;          // false

infer

type Return<T> = T extends (...a: any[]) => infer R ? R : never;
type Arg0<T>   = T extends (a: infer A, ...rest: any[]) => any ? A : never;
type Unwrap<T> = T extends Promise<infer U> ? U : T;
type Elem<T>   = T extends (infer E)[] ? E : never;

Distribution over unions

type ToArray<T> = T extends any ? T[] : never;
type R = ToArray<string | number>;     // string[] | number[]

Naked type param distributes. Wrap with [T] to prevent:

type ToArrayWide<T> = [T] extends [any] ? T[] : never;
type R = ToArrayWide<string | number>;  // (string | number)[]

Filter unions

type Filter<T, U> = T extends U ? T : never;

type Nums = Filter<string | number | boolean, number>;   // number

(That’s basically Extract.)

Mapped types

type Partial2<T> = { [K in keyof T]?: T[K] };
type Readonly2<T> = { readonly [K in keyof T]: T[K] };
type Nullable<T> = { [K in keyof T]: T[K] | null };

Remove modifiers

type Mutable<T> = { -readonly [K in keyof T]: T[K] };
type Required2<T> = { [K in keyof T]-?: T[K] };

-readonly / -? strip the modifier.

Key remapping (as)

type Getters<T> = {
  [K in keyof T as `get${Capitalize<string & K>}`]: () => T[K]
};

type G = Getters<{ id: number; name: string }>;
// { getId: () => number; getName: () => string }

Filter keys via as … never

type StringFields<T> = {
  [K in keyof T as T[K] extends string ? K : never]: T[K]
};

type S = StringFields<{ a: string; b: number; c: string }>;
// { a: string; c: string }

Mapped + conditional combo

type Promisify<T> = {
  [K in keyof T]: T[K] extends (...a: infer A) => infer R
    ? (...a: A) => Promise<R>
    : T[K]
};

DeepPartial

type DeepPartial<T> = T extends object
  ? { [K in keyof T]?: DeepPartial<T[K]> }
  : T;

Paths into nested object

type Path<T> = T extends object
  ? { [K in keyof T & string]: `${K}` | `${K}.${Path<T[K]> & string}` }[keyof T & string]
  : never;

type P = Path<{ a: { b: { c: number } } }>;
// "a" | "a.b" | "a.b.c"

Use carefully — recursive types can blow up.

ValueAt path

type ValueAt<T, P extends string> =
  P extends `${infer K}.${infer Rest}`
    ? K extends keyof T ? ValueAt<T[K], Rest> : never
    : P extends keyof T ? T[P] : never;

Template literal manipulation

type SnakeToCamel<S extends string> =
  S extends `${infer L}_${infer R}`
    ? `${L}${Capitalize<SnakeToCamel<R>>}`
    : S;

type X = SnakeToCamel<"hello_world_foo">;     // "helloWorldFoo"

Length of tuple

type Len<T extends readonly any[]> = T["length"];
type L = Len<[1, 2, 3]>;        // 3

Head / Tail

type Head<T extends any[]> = T extends [infer H, ...any[]] ? H : never;
type Tail<T extends any[]> = T extends [any, ...infer R] ? R : [];

Tuple to union

type T2U<T extends readonly any[]> = T[number];
type U = T2U<["a", "b", "c"]>;     // "a" | "b" | "c"

Union to tuple (use sparingly)

type UnionToIntersection<U> =
  (U extends any ? (x: U) => void : never) extends (x: infer I) => void ? I : never;

type LastOf<U> =
  UnionToIntersection<U extends any ? () => U : never> extends () => infer R ? R : never;

type U2T<U, L = LastOf<U>> = [U] extends [never] ? [] : [...U2T<Exclude<U, L>>, L];

Fragile. Often a sign of bad design — but useful sometimes.

Recursive depth limits

TS limits recursive types to depth ~50. Beyond that, you’ll hit “type instantiation is excessively deep.”

When to stop

If a type is unreadable, switch strategies:

  • runtime check + assertion function
  • code generation
  • accept a wider type

Type-level magic costs maintenance.

Common mistakes

  • Distribution surprise — T extends X ? Y : never over a union.
  • Mutual recursion without depth guard — TS bails.
  • Forgetting to constrain infer — usually fine, but matters for narrowing.
  • & string on keyof T — needed when keys can be symbols.

Read this next

If you want my advanced type utilities, they’re at rajpoot.dev .


Building something AI-, backend-, or data-heavy and want a second pair of eyes? I do consulting and freelance work — see my projects and ways to reach me at rajpoot.dev .