Conditional Types & The infer Keyword
Quick Overview
A conditional type is a ternary the compiler evaluates, choosing one type when `T extends U` and another otherwise. `infer` captures part of the matched shape into a new variable, which is how `ReturnType` extracts a function's result.
Deep Dive
1. Type-Level Ternary Logic
Just as runtime code uses ternary conditionals (condition ? a : b), TypeScript supports Conditional Types in type space:
T extends U ? TrueType : FalseType
2. Conditional Type Basics
type IsString<T> = T extends string ? true : false;
type A = IsString<"hello">; // true
type B = IsString<42>; // false
3. Extracting Types with the infer Keyword
Inside a conditional type's extends clause, the infer keyword declares a type variable that TypeScript will automatically extract and infer:
// Custom utility: Extract array element type
type ElementOf<T> = T extends (infer Element)[] ? Element : T;
type NumberItem = ElementOf<number[]>; // Inferred as: number
type StringItem = ElementOf<string>; // Inferred as: string
// Extract Promise resolved value type
type AwaitedType<T> = T extends Promise<infer Resolved> ? Resolved : T;
type AsyncData = AwaitedType<Promise<{ id: number }>>; // Inferred as: { id: number }
4. Visual Infer Extraction
[ Input Type: Promise<string> ] ──> Matches Promise<infer Resolved>
│
▼
[ Extracted Type ] ──> Resolved = string
5. Common Mistakes
- Using
inferOutside Conditional Extends Clauses: Theinferkeyword CANNOT be used outside a conditional type'sextendsclause!
6. Next Steps
Conditional types power modern state libraries and framework types. Next, let me explain Deep Readonly and Recursive Type Aliases!
Next Up in TypeScript & Type System Mechanics
TypeScript Primitives & Explicit Type Annotations
TypeScript erases entirely at build time, so a `string` annotation produces no runtime check whatsoever. A value arriving from an API is whatever the server actually sent, regardless of what the annotation claims.
Type Inference & Literal Types (const vs. let)
`let x = "GET"` infers `string` because the binding can change; `const x = "GET"` infers the literal `"GET"`. That widening rule is why object properties need `as const` to stay narrow.
Arrays, Tuples & Readonly Modifiers
`string[]` is any number of strings; `[string, number]` is exactly two, in that order. `readonly` removes the mutating methods at compile time, so `push` becomes a type error rather than a runtime surprise.
