Skip to main content

TS Ep 44: The `infer` Keyword (Pattern Matching)

Rachmat Hidayat
Author
Rachmat Hidayat
Learn & sharing insights on TypeScript, Go, Kubernetes, DevOps, DevSecOps, SRE, Platform Engineering, AI/ML Engineering, and MLOps.
typescript - This article is part of a series.
Part 44: This Article
Conditional types evaluate logic (extends). But what if you don’t just want to evaluate a condition, but actually extract a piece of the type you are evaluating? The infer keyword allows you to declare a variable inside a conditional type, unlocking deep pattern matching.

1. How infer Works
#

The infer keyword can only be used inside the extends clause of a conditional type.

It tells the TypeScript compiler: “Try to match this structural pattern. If it matches, extract that specific structural piece and assign it to a new type variable.”

Rebuilding ReturnType<T>
#

Let’s rebuild the built-in ReturnType<T> utility that we used in the Intermediate module. We want to extract whatever a function returns.

// 1. We check if T matches the pattern of a function: T extends (...args: any[]) => ...
// 2. We replace the return 'any' with 'infer R'.
// 3. If T is a function, return the inferred variable R. If not, return never.

type CustomReturnType<T> = T extends (...args: any[]) => infer R ? R : never;

function createSession() {
  return { token: "123", expires: 3600 };
}

// 🟢 Extracts the exact return signature!
type Session = CustomReturnType<typeof createSession>; 
/* Inferred Type: 
{ 
  token: string; 
  expires: number; 
} 
*/

2. Extracting Object Properties
#

You can use pattern matching to extract the type of a specific property from an object, even if you don’t know the full shape of the object.

Imagine an event-driven system where actions optionally have a payload property. We want to write a utility that extracts the payload type, and returns never if there is no payload.

// Pattern Match: Does T have a 'payload' property? If so, extract its type into 'P'.
type ExtractPayload<T> = T extends { payload: infer P } ? P : never;

type Action1 = { type: "LOGIN"; payload: { userId: string } };
type Action2 = { type: "LOGOUT" };

type P1 = ExtractPayload<Action1>; 
// Inferred Type: { userId: string }

type P2 = ExtractPayload<Action2>; 
// Inferred Type: never (The pattern did not match because 'payload' is missing)

3. Parsing Template Literals
#

One of the most jaw-dropping features of modern TypeScript is the ability to use infer inside Template Literal Types to parse and extract parts of a string!

Let’s say we have an authentication system that requires a strictly formatted Bearer <token> string. We want to extract just the token part for our database query.

// Pattern Match: Does T start with "Bearer " followed by any string? 
// If so, extract the rest of the string into 'Token'.
type ExtractBearerToken<T> = T extends `Bearer ${infer Token}` ? Token : never;

type AuthHeader = "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9";

type TokenOnly = ExtractBearerToken<AuthHeader>; 
// Inferred Type: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9"

Advanced Parsing: Route Parameters
#

You can chain infer declarations to build fully-fledged route parsers that extract multiple URL parameters simultaneously!

type ExtractRouteParams<T> = 
  T extends `/users/${infer UserId}/posts/${infer PostId}` 
    ? { userId: UserId; postId: PostId } 
    : never;

type Params = ExtractRouteParams<"/users/99/posts/1024">;

/* Inferred Type:
{
  userId: "99";
  postId: "1024";
}
*/

The infer keyword elevates TypeScript from a simple structural validation tool into a powerful, Turing-complete data extractor at compile time!


Summary & Next Steps
#

In this episode:

  • We learned that infer declares type variables inside extends clauses.
  • We rebuilt ReturnType<T> to extract function outputs.
  • We used structural pattern matching to extract object payloads (infer P).
  • We parsed Template Literal strings to extract JWT tokens and URL route parameters (${infer Token}).

In Episode 45: Inferring Promises (Awaited), we will look at how to use infer recursively to unwrap Promises!

typescript - This article is part of a series.
Part 44: This Article