TypeScriptTypeScript5 min read

TypeScript: Type 'X' is Not Assignable to Type 'Y'

TypeScript throwing type errors? Here's how to understand and fix 'Type X is not assignable to type Y' errors without using 'any'.

David Lee
December 16, 2025
11.2k289

Type 'string | undefined' is not assignable to type 'string'

TypeScript's most frustrating error. Let's fix it properly.

What This Means

You're trying to put a value somewhere, but TypeScript says "nope, wrong type".

let name: string = getName(); // might return undefined
// Error: Type 'string | undefined' is not assignable to type 'string'

Common Scenarios

1. API Responses

interface User {
  name: string;
  email: string;
}

const user = await fetchUser(); // returns User | null
const name: string = user.name; // Error: user might be null

2. Array Methods

const users: User[] = [];
const firstUser: User = users[0]; // might be undefined

3. Object Properties

const config: { theme?: string } = {};
const theme: string = config.theme; // theme is optional

Fix #1: Type Guards

const user = await fetchUser();

if (user) {
  const name: string = user.name; // safe now
}

TypeScript knows inside the if-block, user exists.

Fix #2: Non-Null Assertion (!)

const name: string = user!.name;

Tells TypeScript "trust me, this won't be null". Use carefully.

Only use if you're 100% sure. Otherwise, runtime error.

Fix #3: Nullish Coalescing

const name: string = user?.name ?? 'Default';

Provides fallback if undefined/null.

Fix #4: Type Assertion

const name = user.name as string;

Forces TypeScript to treat it as string. Dangerous - use only when you know better than TypeScript.

Fix #5: Update Type Definition

// Instead of
let name: string;

// Use
let name: string | undefined;

Accepts the reality that it might be undefined.

Real Example: Form Data

I had a form with optional fields:

interface FormData {
  email: string;
  phone?: string;
}

function sendData(email: string, phone: string) {
  // API call
}

const form: FormData = getFormData();
sendData(form.email, form.phone); // Error on phone

Solution 1: Make phone optional in function

function sendData(email: string, phone?: string) {
  // ...
}

Solution 2: Provide default

sendData(form.email, form.phone ?? '');

Solution 3: Type guard

if (form.phone) {
  sendData(form.email, form.phone);
}

The 'any' Trap

Don't do this:

const user: any = await fetchUser();

You just disabled TypeScript. Defeats the purpose.

Union Types

type Status = 'pending' | 'success' | 'error';

let status: Status = 'completed'; // Error

TypeScript says: "completed" isn't one of the allowed values.

Fix: Use correct value

let status: Status = 'success';

Discriminated Unions

Better error handling:

type Result<T> = 
  | { success: true; data: T }
  | { success: false; error: string };

function handleResult(result: Result<User>) {
  if (result.success) {
    // TypeScript knows result.data exists here
    console.log(result.data.name);
  } else {
    // TypeScript knows result.error exists here
    console.log(result.error);
  }
}

Generic Constraints

function getValue<T>(obj: T, key: string): string {
  return obj[key]; // Error: can't index type T
}

Fix with constraint:

function getValue<T extends Record<string, string>>(
  obj: T, 
  key: keyof T
): string {
  return obj[key];
}

My Rule of Thumb

  1. Type Guard - First choice, safest
  2. Nullish Coalescing - When you have a sensible default
  3. Non-Null Assertion - Only if you're certain
  4. Type Assertion - Last resort
  5. any - Never (okay, very rarely)

Strict Mode

If you're not using strict mode:

{
  "compilerOptions": {
    "strict": true
  }
}

Catches more errors early. Painful at first, saves you later.

Bottom Line

TypeScript errors are annoying but they prevent runtime bugs.

Don't fight TypeScript. Fix the actual type issue.

Took me months to stop using 'any' everywhere. Now my code has way fewer bugs.

#TypeScript#Types#Error#Debugging