TypeScript

TypeScript Tutorial

45 lessons

0 / 45 completed0%
Beginner Basics
Intermediate Topics
Advanced Concepts
Lesson 1 of 45
Step 1 of 455 min

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

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".

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

Common Scenarios

1. API Responses

Example
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

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

3. Object Properties

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

Fix #1: Type Guards

Example
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 (!)

Example
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

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

Provides fallback if undefined/null.

Fix #4: Type Assertion

Example
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

Example
// 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:

Example
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

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

Solution 2: Provide default

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

Solution 3: Type guard

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

The 'any' Trap

Don't do this:

Example
const user: any = await fetchUser();

You just disabled TypeScript. Defeats the purpose.

Union Types

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

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

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

Fix: Use correct value

Example
let status: Status = 'success';

Discriminated Unions

Better error handling:

Example
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

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

Fix with constraint:

Example
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:

Example
{
  "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.