This error occurs when TypeScript cannot verify that a value matches an inferred type within a conditional type. It typically happens when using the infer keyword in complex conditional types where type narrowing is insufficient.
This error appears when you're working with TypeScript's conditional types that use the `infer` keyword, and TypeScript's type checker cannot determine that your value satisfies the inferred conditional type. Conditional types allow you to express type relationships using the pattern `T extends U ? X : Y`, and the `infer` keyword lets you capture and name types during this conditional evaluation. The error typically occurs because conditional types can be "deferred" when they depend on type variables. When a conditional type is deferred, TypeScript doesn't know which branch (true or false) will apply until the generic type is resolved. Even if your value is valid for one branch, TypeScript may not be able to prove it's assignable to the overall conditional type without more specific constraints. This is often a limitation in TypeScript's type inference system rather than an actual type safety issue. The compiler takes a conservative approach, preferring to reject potentially valid code rather than allowing potentially unsafe code.
The most common fix is to add more specific constraints that help TypeScript understand which conditional branch applies:
// Before - Too generic
function extract<T>(value: T): T extends Array<infer U> ? U : T {
return Array.isArray(value) ? value[0] : value; // Error!
}
// After - Explicit constraint helps inference
function extract<T extends unknown[]>(value: T): T extends Array<infer U> ? U : never;
function extract<T>(value: T): T;
function extract<T>(value: T): any {
return Array.isArray(value) ? value[0] : value;
}Adding function overloads separates the different cases and gives TypeScript enough information to verify each branch.
When you're certain the type is correct, use type assertions to tell TypeScript explicitly:
function processValue<T>(input: T): T extends Promise<infer U> ? U : T {
if (input instanceof Promise) {
// Type assertion needed here
return input.then(x => x) as T extends Promise<infer U> ? U : T;
}
return input as T extends Promise<infer U> ? U : T;
}This works around TypeScript's conservative inference but requires you to verify correctness manually.
Instead of inline conditional types, extract them into named types or use simpler unions:
// Before - Inline conditional
type Result<T> = T extends Array<infer U> ? U : T;
function process<T>(value: T): Result<T> {
// Complex conditional inference
}
// After - Extract to helper with overloads
type Unwrap<T> = T extends Array<infer U> ? U : T;
function process<T extends any[]>(value: T): T[number];
function process<T>(value: T): T;
function process<T>(value: T): any {
return Array.isArray(value) ? value[0] : value;
}Separating the type logic makes each case explicit and easier for TypeScript to verify.
Ensure your conditional types are distributive over union types when needed:
// Non-distributive - wraps in tuple to prevent distribution
type NonDistributive<T> = [T] extends [Array<infer U>] ? U : T;
// Distributive - applies to each union member
type Distributive<T> = T extends Array<infer U> ? U : T;
// Example usage
type Test1 = NonDistributive<string[] | number>; // string | number
type Test2 = Distributive<string[] | number>; // string | number (distributes)Understanding distribution can help you structure conditionals to work with TypeScript's inference.
Implement type guards that align with your conditional types to help TypeScript narrow:
function isPromise<T>(value: T | Promise<T>): value is Promise<T> {
return value instanceof Promise;
}
function unwrap<T>(value: T | Promise<T>): T extends Promise<infer U> ? U : T {
if (isPromise(value)) {
// TypeScript knows value is Promise<T> here
return value.then(x => x) as any; // Still need assertion
}
return value as any;
}Type guards help but may still require assertions for complex conditional returns.
Break down deeply nested conditionals into intermediate types:
// Before - Nested and complex
type Complex<T> = T extends Promise<infer U>
? U extends Array<infer V>
? V
: U
: T;
// After - Intermediate types
type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
type UnwrapArray<T> = T extends Array<infer V> ? V : T;
type Simplified<T> = UnwrapArray<UnwrapPromise<T>>;
// Easier to debug and understand
function process<T>(value: T): Simplified<T> {
// Implementation
}This makes each inference step explicit and easier for TypeScript to handle.
This error reveals fundamental limitations in TypeScript's type inference system. Conditional types are evaluated lazily - they remain "deferred" when they depend on unresolved type parameters. During this deferred state, TypeScript cannot prove assignability even when it seems obvious to developers.
Theoretical Background: The TypeScript team has acknowledged this as a known limitation in issues like #22860 and #28917. The type checker uses a conservative approach: if both branches of a conditional might apply, it requires the value to be assignable to both, which is often impossible.
Performance Considerations: Complex conditional types with multiple infer keywords can significantly slow compilation. Issues like #2125 document cases where recursive conditional types cause out-of-memory errors. Keep conditionals simple and avoid deep recursion.
Workaround Patterns: The most reliable pattern is to use function overloads instead of conditional return types. This gives TypeScript multiple explicit signatures rather than one complex conditional:
// Preferred for complex cases
function transform<T extends string>(x: T): Uppercase<T>;
function transform<T extends number>(x: T): T;
function transform(x: string | number) {
return typeof x === 'string' ? x.toUpperCase() : x;
}Future TypeScript Versions: Some assignability issues with conditional types have been fixed in recent TypeScript versions. If you encounter this error, check the TypeScript issue tracker and consider upgrading, as improvements to conditional type inference are ongoing.
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