How to fix Type 'X | Y' is not assignable to type 'Z' in TypeScript

TypeScriptINTERMEDIATEMEDIUMUpdated Dec 11, 2025· 113 views

This TypeScript error occurs when you try to assign a union type to a variable or parameter expecting a more specific type. The compiler cannot guarantee type safety without explicit narrowing.

Quick fix

Fixes Type 'X | Y' is not assignable to type 'Z'

// Using typeof for primitive types
function processValue(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows value is string here
    const result: string = value; // ✓ Works
  } else {
    // TypeScript knows value is number here
    const result: number = value; // ✓ Works
  }
}

// Using 'in' operator for object properties
type Dog = { bark: () => void };
type Cat = { meow: () => void };

function handlePet(pet: Dog | Cat) {
  if ('bark' in pet) {
    const dog: Dog = pet; // ✓ Works
  } else {
    const cat: Cat = pet; // ✓ Works
  }
}
  1. Use type guards to narrow the union type
  2. Implement discriminated unions with a common property
  3. Use type assertions when you know the specific type
  4. Add explicit type annotations to prevent unwanted inference
typescript — error
// Using typeof for primitive types
function processValue(value: string | number) {
if (typeof value === 'string') {
// TypeScript knows value is string here
const result: string = value; // ✓ Works
} else {
// TypeScript knows value is number here
const result: number = value; // ✓ Works
}
}
// Using 'in' operator for object properties
type Dog = { bark: () => void };
type Cat = { meow: () => void };
function handlePet(pet: Dog | Cat) {
if ('bark' in pet) {
const dog: Dog = pet; // ✓ Works
} else {
const cat: Cat = pet; // ✓ Works
}
}
Type 'X | Y' is not assignable to type 'Z'

What this error means

This error appears when TypeScript's type checker detects an incompatible assignment involving union types. A union type (X | Y) represents a value that could be either type X or type Y. When you try to assign this union to a variable expecting a specific type Z, TypeScript cannot guarantee that the value will always be compatible with Z. The error is TypeScript's way of preventing potential runtime errors. Since a union type could be any of its constituent types, assigning it to a narrower type without verification could lead to accessing properties or methods that don't exist on all union members. This commonly occurs when working with function parameters, return types, conditional logic, or when TypeScript infers a broader type than you intended. The solution typically involves narrowing the type using type guards, type assertions, or restructuring your code to handle each union member explicitly.

Typical symptoms

  • TypeScript compilation fails with TS2322 error code
  • Error message shows union type (X | Y) not assignable to target type
  • IDE shows red squiggles on variable assignments or function calls
  • Build process fails when attempting to compile TypeScript files
  • Type inference produces broader union types than expected

Common causes

  1. Attempting to assign a union type to a variable expecting only one member of the union
  2. Function parameter accepts specific type but receives union type argument
  3. TypeScript infers a union type from conditional logic or multiple assignments
  4. Missing type narrowing after checking which union member you have
  5. Type guards not properly implemented to discriminate between union members
  6. Object properties have union types but are used as if they were specific types

How to fix "Type 'X | Y' is not assignable to type 'Z'"

1Use type guards to narrow the union type

The most common solution is to use type guards to narrow the union type before assignment. TypeScript supports several type guard patterns:

typescript
// Using typeof for primitive types
function processValue(value: string | number) {
  if (typeof value === 'string') {
    // TypeScript knows value is string here
    const result: string = value; // ✓ Works
  } else {
    // TypeScript knows value is number here
    const result: number = value; // ✓ Works
  }
}

// Using 'in' operator for object properties
type Dog = { bark: () => void };
type Cat = { meow: () => void };

function handlePet(pet: Dog | Cat) {
  if ('bark' in pet) {
    const dog: Dog = pet; // ✓ Works
  } else {
    const cat: Cat = pet; // ✓ Works
  }
}

Type guards let TypeScript understand which specific type you're working with in each branch.

2Implement discriminated unions with a common property

Add a discriminant property to each type in the union to enable automatic type narrowing:

typescript
type Success = { status: 'success'; data: string };
type Error = { status: 'error'; message: string };
type Result = Success | Error;

function handleResult(result: Result) {
  // TypeScript narrows based on discriminant
  if (result.status === 'success') {
    const data: string = result.data; // ✓ Works
  } else {
    const message: string = result.message; // ✓ Works
  }
}

// Switch statements also work
function processResult(result: Result) {
  switch (result.status) {
    case 'success':
      return result.data; // TypeScript knows this is Success
    case 'error':
      return result.message; // TypeScript knows this is Error
  }
}

Discriminated unions are the preferred pattern for complex union types.

3Use type assertions when you know the specific type

If you have external knowledge that a value is a specific type, use type assertions:

typescript
type Animal = Dog | Cat | Bird;

function getDog(animal: Animal): Dog {
  // Use 'as' type assertion
  return animal as Dog;
}

// Or angle bracket syntax (not in JSX)
function getCat(animal: Animal): Cat {
  return <Cat>animal;
}

// Better: combine with runtime check
function getSafeDog(animal: Animal): Dog {
  if ('bark' in animal) {
    return animal as Dog; // ✓ Safe assertion
  }
  throw new Error('Not a dog');
}

Warning: Type assertions bypass TypeScript's type checking. Only use them when you're certain about the type, preferably after runtime validation.

4Add explicit type annotations to prevent unwanted inference

TypeScript sometimes infers union types when you expect a specific type. Add explicit annotations:

typescript
// Problem: TypeScript infers string | number
let value = Math.random() > 0.5 ? 'hello' : 42;
const str: string = value; // ✗ Error

// Solution 1: Explicit annotation
let value: string = Math.random() > 0.5 ? 'hello' : 'world';
const str: string = value; // ✓ Works

// Solution 2: as const for literal types
const config = {
  mode: 'development' // inferred as string
} as const; // now mode is 'development' literal

// Solution 3: Type assertion at initialization
let value = (Math.random() > 0.5 ? 'hello' : 42) as string;

Explicit types help TypeScript understand your intent and prevent overly broad inference.

5Create custom type predicates for complex checks

For reusable type narrowing logic, implement custom type predicate functions:

typescript
interface User {
  id: number;
  name: string;
}

interface Guest {
  sessionId: string;
}

// Type predicate function
function isUser(account: User | Guest): account is User {
  return 'id' in account && 'name' in account;
}

function greet(account: User | Guest) {
  if (isUser(account)) {
    // TypeScript knows account is User
    console.log(`Hello ${account.name}`); // ✓ Works
  } else {
    // TypeScript knows account is Guest
    console.log(`Session: ${account.sessionId}`); // ✓ Works
  }
}

Type predicates are functions that return value is Type and help TypeScript narrow types in calling code.

6Refactor to accept union types or use generics

Sometimes the best solution is to update your code to handle union types properly:

typescript
// Instead of forcing a specific type
function badProcess(value: string) {
  return value.toUpperCase();
}

// Accept the union and handle both cases
function goodProcess(value: string | number): string {
  if (typeof value === 'string') {
    return value.toUpperCase();
  }
  return value.toString();
}

// Or use generics to preserve the type
function identity<T>(value: T): T {
  return value;
}

const str = identity('hello'); // Type: string
const num = identity(42);      // Type: number

Adapting your function signatures to accept union types often produces cleaner, more flexible code than forcing type narrowing everywhere.

Advanced notes

Structural typing implications: TypeScript uses structural typing, meaning type compatibility is determined by structure, not name. A union type X | Y is only assignable to type Z if both X and Y are assignable to Z. This can lead to unexpected errors when types have similar but not identical structures.

Variance and union types: TypeScript unions are invariant in some positions. For example, function parameters are contravariant, which affects how union types can be used in callbacks and event handlers. Understanding variance helps predict when union type assignments will fail.

Excess property checking: When assigning object literals to union types, TypeScript performs excess property checking. This can cause errors even when the object would be structurally compatible, because TypeScript tries to match the literal to a specific union member first.

Union distribution in conditional types: When using conditional types with unions, TypeScript distributes the condition over each union member. This can create unexpected results when trying to assign conditional types involving unions.

Performance considerations: Excessive use of union types with many members can slow down TypeScript compilation. If you have unions with 10+ members, consider using discriminated unions or refactoring into a more hierarchical type structure.

Null and undefined handling: With strictNullChecks enabled, TypeScript treats null and undefined as separate types. This means T | null | undefined requires explicit handling for all three cases. Use optional chaining (?.) and nullish coalescing (??) to simplify null checks.

Frequently asked questions

What does "Type 'X | Y' is not assignable to type 'Z'" mean?
This error appears when TypeScript's type checker detects an incompatible assignment involving union types. A union type (X | Y) represents a value that could be either type X or type Y. When you try to assign this union to a variable expecting a specific type Z, TypeScript cannot guarantee that the value will always be compatible with Z.
How do I fix "Type 'X | Y' is not assignable to type 'Z'"?
This TypeScript error occurs when you try to assign a union type to a variable or parameter expecting a more specific type. The compiler cannot guarantee type safety without explicit narrowing.
What causes "Type 'X | Y' is not assignable to type 'Z'"?
Attempting to assign a union type to a variable expecting only one member of the union Function parameter accepts specific type but receives union type argument TypeScript infers a union type from conditional logic or multiple assignments
Is the "Type 'X | Y' is not assignable to type 'Z'" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading