C.W.K.
Stream
Lesson 06 of 06 · published

`infer` Patterns: Serious Type Magic

~11 min · type-manipulation, infer, advanced, type-extraction

Level 0Curious
0 XP0/93 lessons0/23 achievements
0/100 XP to next level100 XP to go0% complete
"infer plus conditional plus mapped types is the full toolkit. Time to compose them."

What you already know

From track 8: infer declares a type variable inside a conditional pattern. T extends (infer R)[] ? R : T extracts the element type if T is an array. We covered the four canonical patterns: element-of-array, return-of-function, unwrap-promise, first-argument.

This lesson goes further: combining infer with mapped types, template literal types, and recursive patterns. These are the techniques behind most library type definitions you'll encounter.

Multiple infers in one pattern

You can declare multiple infer variables in the same pattern. T extends [infer A, infer B, ...infer Rest] ? [A, B, Rest] : never destructures a tuple into the first two elements plus the rest. Each infer captures a different piece.

Recursive type extraction

Conditional types can be recursive. type Flatten<T> = T extends (infer E)[] ? Flatten<E> : T recursively unwraps arrays. Flatten<string[][][]> evaluates to string. The recursion is bounded by the actual nesting depth — when T isn't an array anymore, recursion stops and returns T.

Template literal pattern matching

Combining infer with template literal types lets you pattern-match strings. T extends `${infer A}-${infer B}` ? [A, B] : never splits 'pippa-quest' into ['pippa', 'quest']. The infer captures the parts between the literal tokens.

The full toolkit: generics, constraints, conditionals, mapped types, template literals, infer. Most advanced library types are some composition of these. Once you can read them, you can build them.

Code

Advanced infer patterns — multiple, recursive, template literal·typescript
// Multiple infers — tuple destructuring.
type FirstTwo<T> = T extends [infer A, infer B, ...unknown[]] ? [A, B] : never;
type A = FirstTwo<[1, 2, 3, 4]>;       // [1, 2]

// Recursive — flatten nested arrays.
type Flatten<T> = T extends (infer E)[] ? Flatten<E> : T;
type B = Flatten<string[][][]>;        // string
type C = Flatten<number>;              // number (base case)

// Recursive — deep readonly.
type DeepReadonly<T> = {
  readonly [K in keyof T]: T[K] extends object ? DeepReadonly<T[K]> : T[K];
};

// Template literal + infer — split string types.
type Split<S extends string, D extends string> =
  S extends `${infer Head}${D}${infer Tail}`
    ? [Head, ...Split<Tail, D>]
    : [S];

type D = Split<'a-b-c-d', '-'>;        // ['a', 'b', 'c', 'd']

External links

Exercise

Write type Reverse<T extends string> that reverses a string at the type level. Reverse<'abc'> should be 'cba'. Use template literal infer with recursion.
Hint
type Reverse<S extends string> = S extends `${infer First}${infer Rest}` ? `${Reverse<Rest>}${First}` : S. The recursion peels one character at a time; the base case is the empty string.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.