0Pricing
TypeScript Academy · Lesson

Type-Level Conditionals

Branch on types with conditional type expressions.

Type-Level Conditionals is a free TypeScript Academy lesson on CoddyKit — lesson 2 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the TypeScript Academy learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.

The Type-Level If

The type language gains branching with conditional types. The syntax T extends U ? X : Y reads: if T is assignable to U, the result is X, otherwise it is Y.

This is the type-level equivalent of an if / else expression.

type IsString<T> = T extends string ? "yes" : "no";

type A = IsString<string>; // "yes"
type B = IsString<number>; // "no"

extends Means Assignable

The test is not equality. T extends U is true when a value of type T could be used where a U is expected. Literal types are assignable to their base type.

type T1 = "hello" extends string ? true : false; // true
type T2 = string extends "hello" ? true : false; // false
type T3 = 42 extends number ? true : false;       // true

Choosing a Result Type

Conditionals let one generic return different shapes depending on input. Here Wrap wraps arrays differently from scalars.

type Wrap<T> = T extends unknown[]
  ? { list: T }
  : { value: T };

type A = Wrap<number>;   // { value: number }
type B = Wrap<string[]>; // { list: string[] }

Filtering to never

A common trick is to return never in one branch. never means "no value", and it is useful for removing members from unions later.

type OnlyStrings<T> = T extends string ? T : never;

type A = OnlyStrings<string>; // string
type B = OnlyStrings<number>; // never

Inferring With infer

The real power appears with the infer keyword. Inside a conditional, infer introduces a fresh type variable that captures part of the matched type.

Here we capture the element type of an array.

type ElementType<T> = T extends (infer U)[] ? U : never;

type A = ElementType<number[]>; // number
type B = ElementType<string[]>; // string

Inferring Function Results

You can place infer anywhere in the pattern. To extract a function return type, infer the part after the arrow. This is how the built-in ReturnType works.

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

type A = MyReturn<() => number>;      // number
type B = MyReturn<(x: string) => boolean>; // boolean

Inferring Multiple Pieces

A single conditional can introduce several infer variables at once. Here we pull both the first and the rest of a tuple.

type FirstRest<T> = T extends [infer H, ...infer R]
  ? { head: H; rest: R }
  : never;

type A = FirstRest<[1, 2, 3]>;
// { head: 1; rest: [2, 3] }

Nested Conditionals

Conditionals nest just like chained else if. The else branch of one conditional can itself be another conditional, forming a decision ladder.

type Describe<T> =
  T extends string ? "text" :
  T extends number ? "num" :
  T extends boolean ? "flag" :
  "other";

type A = Describe<number>; // "num"
type B = Describe<null>;   // "other"

Conditions as Guards

Use a conditional to verify a shape before extracting from it. If the input does not match, fall back to never so misuse is visible.

type GetName<T> = T extends { name: infer N } ? N : never;

type A = GetName<{ name: string }>; // string
type B = GetName<{ age: number }>;  // never

Combining Conditions

You can require multiple conditions by nesting them. Here a type must be both an object and have an id to pass.

type RequireId<T> =
  T extends object
    ? T extends { id: unknown } ? T : never
    : never;

type A = RequireId<{ id: 1; x: 2 }>; // { id: 1; x: 2 }
type B = RequireId<{ x: 2 }>;        // never

Conditionals Are Everywhere

Most built-in utility types are conditionals underneath: NonNullable, Extract, Exclude, Parameters, and ReturnType. Understanding extends ? : unlocks them all.

type MyNonNullable<T> = T extends null | undefined ? never : T;

type A = MyNonNullable<string | null>; // string

Quick Check

Test your understanding of conditional types and infer.

Recap

Conditional types give the type language an if/else and pattern matching.

  • T extends U ? X : Y branches on assignability.
  • infer captures matched parts in fresh variables.
  • Conditionals nest to form decision ladders.
  • Returning never filters members out.

Next: feeding a conditional back into itself for recursion.

Frequently asked questions

Is the “Type-Level Conditionals” lesson free?

Yes — the full text of “Type-Level Conditionals” is free to read here on the web, and the TypeScript Academy course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the TypeScript Academy course, upgrade to CoddyKit PRO.

What will I learn in “Type-Level Conditionals”?

Branch on types with conditional type expressions. You practise TypeScript Academy with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.

Do I need any experience to start TypeScript Academy?

No prior experience is required. TypeScript Academy on CoddyKit is structured for beginners through advanced learners; this is — lesson 2 of 4, so you can start here or from the beginning and move at your own pace.

How long does the “Type-Level Conditionals” lesson take?

Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.

Can I write and run code in this TypeScript Academy lesson?

Yes. Every TypeScript Academy lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.

All lessons in this course

  1. Types as a Computation Language
  2. Type-Level Conditionals
  3. Type-Level Recursion
  4. Distributive Conditional Types
← Back to TypeScript Academy