How to fix Type 'T extends U ? X : Y' is not assignable to type 'Z' in TypeScript

TypeScriptADVANCEDMEDIUMUpdated Dec 8, 2025ยท 101 views

This error occurs when TypeScript cannot guarantee that both branches of a conditional type are assignable to a target type. The fix involves understanding type constraints, distributivity, and ensuring all conditional branches satisfy the target type.

Quick fix

Fixes TS2322

// Problem: conditional type not assignable
type Result = T extends string ? string : number;
const value: string | number = /* conditional */ Result; // TS Error

// Solution: Check each branch
type TrueBranch = string;  // OK for string | number
type FalseBranch = number; // OK for string | number
// Both branches ARE assignable, so the issue is deeper
  1. Analyze both conditional branches
  2. Add extends constraints to prevent distributivity
  3. Ensure target type accommodates all conditional outcomes
  4. Add proper extends constraints to generic parameters
typescript โ€” error
// Problem: conditional type not assignable
type Result = T extends string ? string : number;
const value: string | number = /* conditional */ Result; // TS Error
// Solution: Check each branch
type TrueBranch = string; // OK for string | number
type FalseBranch = number; // OK for string | number
// Both branches ARE assignable, so the issue is deeper
Type 'T extends U ? X : Y' is not assignable to type 'Z'

What this error means

When you create a conditional type with the syntax `T extends U ? X : Y`, TypeScript checks whether type `T` is assignable to type `U`. If yes, it resolves to `X`; otherwise it resolves to `Y`. The error "Conditional type is not assignable to" appears when TypeScript assigns or uses a conditional type but cannot prove that both the true branch (`X`) and false branch (`Y`) are assignable to the target type. This often happens because: 1. **Type narrowing in one branch**: The true branch narrows types differently than the false branch, creating an assignability mismatch 2. **Generic constraints ignored**: TypeScript's type resolution may ignore type parameter constraints 3. **Distributive conditional types**: When a naked type parameter triggers distributive behavior over unions, the result may not match the target 4. **Deferred vs. resolved conditionals**: If the condition depends on type variables, TypeScript may defer resolution, causing assignability issues

Typical symptoms

  • TypeScript error appears when assigning a conditional type to a variable or parameter
  • The error mentions both the conditional type and the target type it cannot be assigned to
  • Wrapping the conditional type in a helper function sometimes works but direct assignment fails
  • The error occurs even though both branches seem compatible with the target type
  • Works correctly without the conditional type, but breaks when added
  • Generic functions with conditional return types fail to satisfy their declared return type

Common causes

  1. Target type is too narrow for one branch of the conditional (most common)
  2. Type narrowing in the true branch doesn't apply to the false branch
  3. Using a naked type parameter in a distributive conditional when non-distributive behavior is needed
  4. Type parameter constraints are not considered in conditional type resolution
  5. The conditional type contains type variables that prevent resolution at assignment time
  6. Generic type parameters lack proper extends constraints to narrow possibilities
  7. Both branches of the conditional are correct individually but don't unify to the target type

How to fix "Type 'T extends U ? X : Y' is not assignable to type 'Z'"

1Analyze both conditional branches

First, understand what your conditional type resolves to in each case. Extract each branch separately and verify they match the target type:

typescript
// Problem: conditional type not assignable
type Result = T extends string ? string : number;
const value: string | number = /* conditional */ Result; // TS Error

// Solution: Check each branch
type TrueBranch = string;  // OK for string | number
type FalseBranch = number; // OK for string | number
// Both branches ARE assignable, so the issue is deeper

Use TypeScript's type checking to isolate which branch is problematic.

2Add extends constraints to prevent distributivity

If your conditional type uses a naked type parameter, it becomes distributive over unions. Wrap the parameter in square brackets to disable distribution:

typescript
// Distributive (problematic in many cases)
type ToArray<T> = T extends any ? T[] : never;
type Result = ToArray<string | number>; // (string | number)[]

// Non-distributive (correct)
type ToArrayNonDist<T> = [T] extends [any] ? T[] : never;
type Result = ToArrayNonDist<string | number>; // (string | number)[]

Distribution is useful for some patterns but can cause unexpected behavior when assigned to narrow types.

3Ensure target type accommodates all conditional outcomes

Make sure your target type is a union or broad enough to accept both branches:

typescript
// Problem: target type too narrow
type Conditional<T> = T extends string ? string : number;
const x: string = Conditional<boolean>; // TS Error: number not assignable to string

// Solution: Use union for target
const x: string | number = Conditional<boolean>; // OK

// Or: Use unknown/any if truly flexible (not recommended)
const x: unknown = Conditional<boolean>; // OK but loses type safety

The target type must be wide enough to accept both the true and false branches.

4Add proper extends constraints to generic parameters

Constrain your generic type parameters to help TypeScript understand valid inputs:

typescript
// Problem: no constraint, TypeScript can't narrow
type Extract<T> = T extends string ? T : never;
const x: string = Extract<string | number>; // TS Error

// Solution: constrain T
type ExtractString<T extends string | number> = T extends string ? T : never;
const x: string = ExtractString<"hello">; // OK
const y: string = ExtractString<string>; // OK

Constraints help TypeScript understand which inputs are valid and resolve the type more accurately.

5Use conditional types at definition, not assignment

Instead of assigning a conditional type to a narrowly-typed variable, use the conditional in the type definition itself:

typescript
// Problem: conditional assigned to narrow type
function process<T>(value: T): string {
  const result: string = T extends string ? value : "default"; // TS Error
  return result;
}

// Solution: Use conditional in return type
function process<T>(value: T): T extends string ? string : string {
  return T extends string ? value : "default"; // OK
}

// Or: More specific
function process<T extends string | number>(value: T): T extends string ? T : string {
  return value as any;
}

Let the conditional determine the type rather than forcing it into a pre-determined shape.

6Check for deferred conditional types

If your conditional type depends on unresolved type variables, TypeScript may defer it. Ensure variables are properly resolved:

typescript
// Problem: deferred conditional
type Flatten<T> = T extends Array<infer U> ? U : T;
// This is deferred because it depends on T

// Use in function
function flatten<T>(arr: T): Flatten<T> {
  // TS may not resolve Flatten<T> at assignment time
  return arr[0]; // Possible error
}

// Solution: Resolve with concrete types
function flatten<T extends Array<any>>(arr: T): T extends Array<infer U> ? U : T {
  return arr[0];
}

Make sure all type variables in your conditional are known at the point of use.

Advanced notes

### Understanding Distributive Conditional Types

When a conditional type's condition checks a naked type parameter (T extends U), it distributes over union types:

typescript
type ToArray<T> = T extends any ? T[] : never;

// T = string | number
// Distributes as: (string extends any ? string[] : never) | (number extends any ? number[] : never)
// Result: string[] | number[]
type Result = ToArray<string | number>; // string[] | number[]

This is often desired, but can cause "not assignable" errors when:
- The target type expects non-distributed behavior
- Union members have conflicting assignability
- The distribution creates a union incompatible with the target

### Constraint Behavior in Conditionals

TypeScript deliberately ignores type parameter constraints when resolving conditionals to prevent unexpected behavior:

typescript
type Check<T extends string> = T extends any ? T : never;
// T is constrained to string, but the conditional doesn't use that constraint
// This prevents complexity but can feel counterintuitive

If you need constraints to affect resolution, make them explicit in the conditional:

typescript
type Check<T extends string> = T extends string ? T : never; // Now uses constraint

### Common Pattern: Conditional Type with Default

A common pattern is creating a conditional that safely handles all inputs:

typescript
type SafeExtract<T, Fallback = never> = T extends string ? T : Fallback;

// Usage with concrete fallback
const result: string | null = SafeExtract<string | number, null>; // OK

### Recursive Conditional Types

Conditional types cannot reference themselves recursively directly:

typescript
// Invalid: would be infinite
type Flatten<T> = T extends Array<infer U> ? Flatten<U> : T;

// Workaround: use helper type (valid)
type FlattenHelper<T, Depth extends number = 1> =
  Depth extends 0 ? T :
  T extends Array<infer U> ? FlattenHelper<U, [-1, 0, 1, 2][Depth]> : T;

Frequently asked questions

What does "TS2322" mean?
When you create a conditional type with the syntax T extends U ? X : Y, TypeScript checks whether type T is assignable to type U. If yes, it resolves to X; otherwise it resolves to Y.
How do I fix "TS2322"?
This error occurs when TypeScript cannot guarantee that both branches of a conditional type are assignable to a target type. The fix involves understanding type constraints, distributivity, and ensuring all conditional branches satisfy the target type.
What causes "TS2322"?
Target type is too narrow for one branch of the conditional (most common) Type narrowing in the true branch doesn't apply to the false branch Using a naked type parameter in a distributive conditional when non-distributive behavior is needed
Is the "TS2322" error serious?
It is a medium-severity error โ€” more of a blocker than a risk. The steps above resolve it.

Official resources & further reading