Conditional Types
Advanced TypeScript•Section 3 of 4•advanced
Topic Overview
Take your time to understand each concept. Practice with the examples below!
Conditional types are like if-else statements for types! They let you create types that change based on conditions, making your type system incredibly flexible and powerful.
What are Conditional Types? Conditional types use the same ternary operator (? :) as JavaScript, but for types instead of values. They follow the pattern: T extends U ? X : Y
How They Work:
1. Check if type T matches condition U 2. If yes, the type becomes X 3. If no, the type becomes Y 4. Just like if-else, but at the type level!
Basic Examples:
- •Is it a string? T extends string ? true :false
- •Nullable check:T extends null | undefined ? never : T
- •Array check:T extends any[] ? T[number] : T
The Power of 'infer':
The infer keyword lets you extract types from other types:
- •Get return type:T extends (...args: any[]) => infer R ? R : never
- •Get array element:T extends (infer U)[] ? U : T
- •Extract promise type:T extends Promise<infer U> ? U : T
Real-World Use Cases:
- •API response handling:Different types based on status
- •Form validation:Type based on field type
- •Component props:Conditional required fields
- •State machines:Type based on current state
Distributive Conditional Types:
When used with unions, conditional types distribute:
- •Input:string | number
- •Condition:T extends string ? 'text' : 'other'
- •Result:'text' | 'other'
Advanced Patterns:
- •Exclude null:NonNullable<T>
- •Extract keys:Extract<T, U>
- •Filter types:Exclude<T, U>
- •Type guards:T extends U ? T : never
Why Use Conditional Types?
- •Create smart types that adapt
- •Build powerful utility types
- •Type-safe conditional logic
- •Reduce code duplication
- •Express complex type relationships
Example
// Basic conditional typetype IsString<T> = T extends string ? true : false;type Test1 = IsString<string>; // truetype Test2 = IsString<number>; // false// More practical exampletype NonNullable<T> = T extends null | undefined ? never : T;type SafeString = NonNullable<string | null>; // stringtype SafeNumber = NonNullable<number | undefined>; // number// Conditional type with infertype ReturnType<T> = T extends (...args: any[]) => infer R ? R : never;function getString(): string { return "hello"; }function getNumber(): number { return 42; }type StringReturn = ReturnType<typeof getString>; // stringtype NumberReturn = ReturnType<typeof getNumber>; // number// Distributive conditional typestype ToArray<T> = T extends any ? T[] : never;type StringOrNumberArray = ToArray<string | number>; // string[] | number[]// Complex conditional type exampletype ApiResponse<T> = T extends string ? { message: T } : T extends number ? { code: T } : T extends boolean ? { success: T } : { data: T };type MessageResponse = ApiResponse<string>; // { message: string }type CodeResponse = ApiResponse<number>; // { code: number }type SuccessResponse = ApiResponse<boolean>; // { success: boolean }type DataResponse = ApiResponse<object>; // { data: object }console.log("Conditional types provide powerful type transformations!");