`T extends U ? A : B` — `if` for Types
Conditional Types
Conditional types pick one of two types based on whether a condition holds. The foundation of advanced type manipulation.
What you'll learn
- Read conditional-type syntax
- Use them with generics
- Recognize distribution over unions (next lesson)
A conditional type is a type-level ternary: T extends U ? A : B.
The Basics
type IsString<T> = T extends string ? true : false;
type A = IsString<"hi">; // true
type B = IsString<42>; // false
type C = IsString<string>; // true (string is assignable to string) extends here means “is assignable to”. When the condition holds,
the type is A; otherwise B.
A Common Pattern — Default Extraction
type NonNull<T> = T extends null | undefined ? never : T;
type A = NonNull<string | null>; // string (null → never, filtered out)
type B = NonNull<number | undefined>; // number A simple version of the built-in NonNullable<T>.
Pulling the Function Return Type
type ReturnTypeOf<T> = T extends (...args: any) => infer R ? R : never;
type R = ReturnTypeOf<() => User>; // User infer R lets you extract a type from inside the matched pattern.
Full lesson next.
Nested Conditionals
type Width<T> =
T extends string ? "short" :
T extends number ? "exact" :
T extends boolean ? "binary" :
"other";
type A = Width<"hi">; // "short"
type B = Width<42>; // "exact"
type C = Width<true>; // "binary"
type D = Width<symbol>; // "other" Stacking conditionals reads like a type-level switch.
With Generics
The most useful form — conditional + generic:
function unwrap<T>(value: T): T extends Promise<infer U> ? U : T {
// ...
return value as any;
}
unwrap("hi"); // string
unwrap(Promise.resolve(42)); // number The return type depends on what was passed.
When To Use Conditionals
- Returning different types based on input
- Filtering union members
- Building utility types (most of
Partial,Pick, etc., use them internally) - Extracting parts of types via
infer
Up Next
infer in depth.