Volume 4

Q208: Conditional Types and the infer Keyword

Difficulty: SeniorFrequency: MediumAnswer time: 8-12 minutes

What Interviewers Want To Evaluate

Interviewers want to know whether you can read and design conditional types without turning the codebase into type gymnastics.

They check conditional type syntax, distributive behavior, infer, extracting return values, unwrapping promises, filtering unions, and practical API design.

Short Interview Answer

Conditional types choose one type or another based on assignability, using syntax like T extends U ? X : Y. They are useful for extracting, filtering, and transforming types. The infer keyword lets TypeScript capture part of a type inside the true branch. I use these tools for library-level helpers and shared abstractions, but I avoid deep conditional chains in everyday app code.

Detailed Interview Answer

A conditional type looks like a ternary expression for types.

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

Usage:

type A = IsString<"hello">;
type B = IsString<number>;

A is true.

B is false.

Assignability Check

The condition is based on assignability.

type HasId<T> = T extends { id: string } ? true : false;
type UserHasId = HasId<{ id: string; name: string }>;
type NumberHasId = HasId<number>;

The first is true.

The second is false.

Extracting Data

Conditional types can extract useful information.

type DataOf<T> = T extends { data: unknown } ? T["data"] : never;

Example:

type ApiResponse = {
  data: { id: string; name: string };
};

type UserData = DataOf<ApiResponse>;

This is useful when many wrappers share a pattern.

infer

infer captures a type variable from a matched structure.

type ArrayItem<T> = T extends Array<infer Item> ? Item : never;

Usage:

type User = ArrayItem<{ id: string }[]>;

User becomes { id: string }.

You did not manually index the array.

You asked TypeScript to infer the item.

Return Type With infer

The built-in ReturnType works like this conceptually:

type MyReturnType<T> = T extends (...args: never[]) => infer Return
  ? Return
  : never;

Usage:

function createConfig() {
  return {
    theme: "dark",
    density: "compact",
  };
}

type Config = MyReturnType<typeof createConfig>;

This extracts the function return shape.

Promise Unwrapping

You can unwrap promises.

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

Usage:

type LoadedUser = UnwrapPromise<Promise<{ id: string }>>;

Real TypeScript has Awaited<T>, which handles nested thenables more completely.

Prefer built-ins when they fit.

Distributive Conditional Types

Conditional types distribute over unions when the checked type is a naked type parameter.

type ToArray<T> = T extends unknown ? T[] : never;

type Result = ToArray<string | number>;

The result is:

string[] | number[]

Not:

(string | number)[]

This behavior powers union filtering.

Filtering Unions

Exclude and Extract are conditional types.

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

Example:

type Status = "idle" | "loading" | "success" | "error";
type Terminal = MyExclude<Status, "idle" | "loading">;

The union is distributed member by member.

Preventing Distribution

Sometimes distribution is not wanted.

Wrap the checked type in a tuple.

type IsUnionAssignable<T, U> = [T] extends [U] ? true : false;

This checks the union as a whole.

Knowing this trick helps when reading advanced utility types.

Practical API Result Example

Consider API states.

type ApiResult<TData, TError = string> =
  | { ok: true; data: TData }
  | { ok: false; error: TError };

Extract successful data:

type SuccessData<T> = T extends { ok: true; data: infer Data } ? Data : never;

Usage:

type UserResult = ApiResult<{ id: string; name: string }>;
type User = SuccessData<UserResult>;

This is useful for shared data libraries.

Practical Component Example

You can extract props from a component type.

type PropsOf<T> = T extends (props: infer Props) => React.ReactNode
  ? Props
  : never;

This can help library authors.

In application code, explicit exported prop types are often clearer.

Common Mistakes

Common mistakes include:

using conditional types for simple unions
forgetting distribution over unions
not knowing how to prevent distribution
building recursive types that slow the compiler
using infer when indexed access is enough
making app code depend on unreadable helper types

Conditional types should earn their complexity.

Senior Trade-Offs

Conditional types are strongest in:

shared libraries
framework adapters
API wrappers
schema helpers
event systems
utility types
component libraries

They are weaker when a direct type alias communicates the idea better.

In a product codebase, readability matters more than type cleverness.

Interview Framing

In an interview, say:

Conditional types let me transform types based on assignability. I use infer to capture inner types such as array items, promise values, function returns, or wrapped API data. I am careful because distributive behavior and deep helpers can surprise teams and slow down type checking.

Revision Notes

  • Conditional types use T extends U ? X : Y.
  • infer captures a type inside the matched branch.
  • Naked type parameters distribute over unions.
  • Tuple wrapping can prevent distribution.
  • Many built-in utilities use conditional types.
  • Advanced helpers should be readable and necessary.

Follow-Up Questions

  • What does infer do?
  • Why does a conditional type distribute over unions?
  • How can you prevent distribution?
  • How would you extract the item type from an array?
  • When should you avoid conditional types?

How I Would Answer This In A Real Interview

I would say conditional types are useful when a type needs to branch based on another type's shape. infer lets TypeScript capture inner pieces like array items, promise values, or return types. I would mention distributive behavior because it is important for filtering unions. Then I would add that I keep these helpers mostly in shared libraries or type utilities, not scattered through everyday feature code.