This error occurs when TypeScript's type inference cannot determine the type of a variable within a conditional type clause. The fix typically involves providing explicit type hints or restructuring the conditional type to give TypeScript enough information to infer the type.
Conditional types in TypeScript use the syntax `T extends Condition ? TrueType : FalseType` to select different types based on whether T matches a condition. When you use the `infer` keyword to capture types within the condition, TypeScript must match and extract the type variable. This error occurs when TypeScript cannot determine what type to infer in the condition branch. This happens because: - The type structure doesn't provide enough information for pattern matching - The inferred type appears in the false branch (where it's not allowed) - The generic type parameter isn't constrained enough - The conditional type is applied in a context where TypeScript can't deduce the type
The infer keyword can only be used in the true branch of a conditional type. Inferred type variables are not available in the false branch.
Wrong:
type ExtractArrayElement<T> = T extends any[] ? string : T[]; // Error: T doesn't exist in false branchCorrect:
type ExtractArrayElement<T> = T extends (infer E)[] ? E : T;The inferred type E is only available in the true branch (E), not in the false branch (T).
The pattern in the extends clause must match the structure of T for inference to work. Make sure the pattern is complete.
Problem:
type GetReturnType<T> = T extends (...args: any[]) => infer R ? R : never;
const result: GetReturnType<string>; // Error: string doesn't match function patternFix: Add a constraint or provide the right type:
type GetReturnType<T extends (...args: any[]) => any> = T extends (...args: any[]) => infer R ? R : never;
const result: GetReturnType<() => string>; // OKThe constraint extends (...args: any[]) => any ensures T is always a function, so inference will work.
When using conditional types with union types, they distribute over each union member. This can affect inference.
Example:
type ToArray<T> = T extends any ? T[] : never;
type A = ToArray<string | number>; // (string | number)[] | (string | number)[] = string[] | number[]If you don't want distribution, wrap the generic in brackets:
type ToArray<T> = [T] extends [any] ? T[] : never;
type B = ToArray<string | number>; // (string | number)[]If TypeScript can't infer the type from usage, provide explicit type arguments:
Problem:
function processArray<T>(arr: T[]): T extends any[] ? 'nested' : 'flat' {
// Error: T is not inferred from the parameter
return 'flat' as any;
}
const result = processArray([1, 2, 3]); // Type of T is ambiguousFix: Use explicit type argument:
const result = processArray<number>([1, 2, 3]); // OKOr restructure to make inference work automatically:
function processArray<T extends any[]>(arr: T): T extends (infer E)[] ? 'nested' : 'flat' {
return (E extends any[] ? 'nested' : 'flat') as any;
}
const result = processArray([1, 2, 3]); // Inference worksDeeply nested conditional types can confuse TypeScript's inference. Break them into smaller, reusable types:
Problem:
type Complex<T> = T extends any[]
? T extends (infer E)[]
? E extends string
? infer U // Error: U not properly inferred in context
: never
: never
: never;Fix: Split into helper types:
type IsArray<T> = T extends any[] ? true : false;
type ArrayElement<T extends any[]> = T extends (infer E)[] ? E : never;
type IsString<T> = T extends string ? true : false;
type Simplified<T> = IsArray<T> extends true
? IsString<ArrayElement<T>> extends true
? ArrayElement<T>
: never
: never;This is clearer and gives TypeScript better opportunities to infer types.
TypeScript 5.0+ allows constraints on inferred type parameters for more precise inference:
Modern approach (TypeScript 5+):
type ExtractStringArrayElement<T extends string[]> = T extends (infer E extends string)[] ? E : never;
const result = ExtractStringArrayElement<['a', 'b']>; // 'a' | 'b'This constraint infer E extends string tells TypeScript that E must be a string, improving inference accuracy.
### Type Inference Algorithm
TypeScript's conditional type inference uses a pattern-matching algorithm:
1. Compare T against the extends condition
2. If T matches, capture any inferred types using infer
3. Use captured types in the true branch
4. If no match, use false branch
If TypeScript can't confidently determine the match (e.g., T is too generic), inference fails.
### Common Patterns That Work
Extracting Array Elements:
type ArrayElement<T> = T extends (infer E)[] ? E : T;Extracting Function Return Type:
type ReturnType<T> = T extends (...args: any[]) => infer R ? R : any;Extracting Promise Value:
type Awaited<T> = T extends Promise<infer U> ? U : T;### When Inference Fails
Inference often fails when:
- The generic isn't directly used in a way that constrains its structure (e.g., passed as-is to a function)
- You're trying to infer from an unstructured type like unknown or a bare generic
- The conditional type is deferred (TypeScript doesn't evaluate it immediately)
### TypeScript Versions
This error has been present since conditional types were introduced (TypeScript 2.8). Some inference improvements were made in TypeScript 4.7+ with better handling of type inference in nested contexts.
Refer to the TypeScript release notes for version-specific improvements.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript