infer — Pattern Matching for Types

Advanced · 20 min read · ▶ live playground · ✦ checkpoint

infer lets a conditional type name a piece of the shape it matched. T extends Promise<infer Value> ? Value : never means: if T fits Promise<...>, call the inside type Value and return it.

Use it when indexed access cannot name the piece directly, but a pattern can.

infer names the blank

An infer variable is a temporary type name created inside the extends side of a conditional type. Think of it as a labeled blank in a pattern: Promise<infer Value> says, "match a Promise, and write whatever is inside into the blank named Value."

type UnwrapPromise<T> = T extends Promise<infer Value> ? Value : never;

Read it with last lesson's conditional-type voice: "for some type T, if T fits Promise<Value>, return Value; otherwise return never."

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

UnwrapPromise<Promise<LaunchPayload>> returns the payload type. UnwrapPromise<string> would return never, because a plain string does not match the Promise<...> pattern.

infer does not inspect runtime promises. It works on types before the program runs, then erases like the rest of the type layer.

Rebuild ReturnType and Parameters

Functions have a shape too: parameters on the left, return type on the right. infer can name both pieces.

These are tiny teaching copies of the built-in utility types. Section 12.3 owns the real wrapper-code uses.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

MyReturnType matches a function pattern and names the return piece Result. MyParameters matches the same kind of function pattern and names the parameter tuple Args.

A parameter tuple is a tuple type that stores a function's parameter types in order. For BuildReceipt, ReceiptArgs is [email: string, plan: "free" | "pro"], so the rest parameter in buildReceipt(...args: ReceiptArgs) gets the same call shape.

infer can have a constraint

Sometimes you want to name a piece only if that piece also fits a smaller shape. An inferred constraint is infer Name extends Shape, which means "fill this blank, but only with a type assignable to Shape."

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

The first two union members have payload objects with a string kind, so Payload is allowed and Payload["kind"] gives "email" | "sms". The legacy member has payload: "legacy", which does not fit { kind: string }, so that branch becomes never.

This is still the same conditional-type machine from 10.3: distribute over the union, match each member, return the piece you want, and let never disappear.

infer only works inside the pattern part of a conditional type. You cannot use it as a free-standing type name.

type Bad = infer Value;

→ playground.ts:1:12 - error TS1338: 'infer' declarations are only permitted in the 'extends' clause of a conditional type.

Stop before the chain turns clever

Because infer can name nested pieces, it can tempt you into writing a whole puzzle in one alias. The checker may understand it; the next person may not.

typescript — playgroundtype-checked
⌘/Ctrl + Enter to run

The aliases are deliberately split:

  • HandlerRequest<T> extracts the request.
  • HandlerResponse<T> extracts the promised response.
  • RequestUser<T> extracts the user from the request.

You could jam that into one long conditional type. Do not reach for that first. Name the middle when the pattern has a story, just like 10.2's rule for deep indexed access chains.

Now you can inspect types, pull pieces out, and choose branches. Section 11 turns from inspection to transformation: mapped types walk every key in a shape and build a new shape from it.

Checkpoint

Answer all three to mark this lesson complete

+50 XP on completion