How to Use Conditional Types in TypeScript
Use conditional types in TypeScript when a type should change based on another type’s shape or value relationship. They are perfect for reusable utilities, API helpers, schema transforms, and advanced library typing.
What you’ll build or solve
You’ll learn how to use conditional types in TypeScript with extends, infer, and reusable generic utilities. You’ll also know when they are better than unions alone.
Learn TypeScript on Mimo
When this approach works best
This approach is the right choice when generic type behavior depends on input types.
Common real-world scenarios include:
- API response helpers
- Promise unwrapping
- Nullable transforms
- Function return extraction
- Library utility types
This is a bad idea when a simple union or overload communicates the intent more clearly.
Prerequisites
You only need:
- Strong TypeScript generics knowledge
extendsconstraints- Familiarity with utility types
Step-by-step instructions
Step 1: Create a basic conditional type
The core syntax is:
type IsString<T> = T extends string ? true : false;
This checks whether T matches string.
type A = IsString<string>;
type B = IsString<number>;
This becomes:
A = trueB = false
Step 2: Use conditional types for reusable transforms
A common pattern is nullable cleanup.
type NonNullableValue<T> = T extends null | undefined ? never : T;
This removes nullish values from unions.
It is great for reusable library helpers.
Step 3: Use infer to extract nested types
infer makes conditional types much more powerful.
type GetReturnType<T> = T extends (...args: never[]) => infer R
? R
: never;
This extracts function return types automatically.
type Result = GetReturnType<() => number>;
This becomes number.
Step 4: Unwrap promises
A very practical use case is promise extraction.
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
This is excellent for async helpers.
What to look for:
- Use
T extends X ? A : B - Great for reusable generic utilities
inferextracts nested types- Excellent for async and function helpers
- Keep them readable
Examples you can copy
String check
type IsString<T> = T extends string ? true : false;
Promise unwrap
type Data = UnwrapPromise<Promise<User>>;
Return extraction
type Result = GetReturnType<typeof loadUser>;
Common mistakes and how to fix them
Mistake 1: Using conditional types for simple unions
What the reader might do:
Create complex conditional logic for tiny APIs.
Why it breaks: readability drops.
Corrected approach:
Use unions or overloads first.
Mistake 2: Forgetting distributive behavior on unions
What the reader might do:
Apply a conditional type to string | number.
Why it breaks: the result distributes across the union.
Corrected approach:
Use tuple wrappers when distribution is unwanted.
Mistake 3: Overusing nested infer chains
What the reader might do:
Build deeply nested conditional helpers.
Why it breaks: maintenance becomes hard.
Corrected approach:
Break utilities into smaller named helpers.
Troubleshooting
If unions split unexpectedly, check distributive behavior.
If extraction fails, verify the infer position.
If the helper feels unreadable, simplify the generic.
If the use case is basic, use overloads instead.
Quick recap
- Use
extends ? :for conditional types - Great for reusable generic transforms
- Use
inferto extract nested types - Excellent for promise and function helpers
- Keep utilities small and readable
Join 35M+ people learning for free on Mimo
4.8 out of 5 across 1M+ reviews
Check us out on Apple AppStore, Google Play Store, and Trustpilot