Skip to content

TypeScript errorTypeScript: “Type 'X' is not assignable to type 'Y'” (TS2322)

The compiler stops with Type 'string | undefined' is not assignable to type 'string' (or Type 'string' is not assignable to type '"small" | "large"', or a long nested message about objects). A value of one type is being put where TypeScript expects another, and it cannot prove the two are compatible.

Short answer

Read the error from the bottom: the last indented line names the exact property or value that does not fit. If the mismatch is undefined or null, handle that case first (a check, a default with ??, or an optional type). If a string should be one of a few values, type it as that union from the start, or add as const to the literal. If an object is missing a property, add it or make the target property optional. Use an as cast only when you know something TypeScript cannot, because a wrong cast hides a real bug.

Why the types don’t match, and the fix

  1. 01

    The value can be undefined or null

    With strict mode on, string | undefined does not fit into string. Values from optional props, find(), Map.get(), process.env and form fields are often like this. Check for the missing case, give a default, or let the target accept undefined.

    const user = users.find((u) => u.id === id);
    if (!user) throw new Error(`No user ${id}`);
    setName(user.name); // user is User here
    
    const port: string = process.env.PORT ?? '3000';
  2. 02

    An object is missing a property, or has the wrong shape

    “The basic rule for TypeScript’s structural type system is that x is compatible with y if y has at least the same members as x.” TypeScript, Type compatibility

    TypeScript compares objects by their properties, not by their names. An object fits a type when it has at least the properties that type requires, with compatible types. The nested lines of the error point to the property that differs.

    interface Props { title: string; count: number }
    
    // error: property count is missing
    // <Card title="Inbox" />
    <Card title="Inbox" count={unread.length} />
  3. 03

    A string where a union of literals is expected

    let size = 'small' is typed as string, which can hold any text, so it does not fit a prop typed 'small' | 'large'. Type the variable with the union, or use const or as const so TypeScript keeps the literal value.

    type Size = 'small' | 'large';
    let size: Size = 'small';
    
    const options = { size: 'small' } as const;
    <Button size={options.size} />
  4. 04

    Using as to force it

    “TypeScript only allows type assertions which convert to a more specific or less specific version of a type.” TypeScript, Everyday types: type assertions

    An as cast tells TypeScript to trust you, and it only allows casts between related types. If you need as unknown as Something to make the error go away, the types really do not match: fix the data or the type instead.

    const canvas = document.getElementById('chart') as HTMLCanvasElement; // fine when you know the element
    
    // a warning sign:
    const user = response as unknown as User;

Checked against the vendor’s docs on : TypeScript Handbook: Type compatibility; TypeScript Handbook: Everyday types.

Rather hand it over?

Fixed scope, fixed price, paid upfront. The work happens in writing; no call needed.

Related: TypeScript “Property does not exist on type” · TypeScript “Cannot find name” · “Cannot read properties of undefined”

Questions

More on this error

Should I turn off strict mode to get rid of these errors?

No. Strict mode is what catches the undefined and null cases that crash at runtime. Fix the few places it flags; turning it off hides them all.

The error message is 20 lines long. Where do I look?

At the last indented line. TypeScript prints the outer types first and the exact property that does not fit last.

Can you fix it for me?

If one bug is the whole problem, the App Fix Ticket covers it: a written diagnosis first, then the fix. If the app has more going on, the Launch Check is a written check of the whole app. Each issue found is rated critical, high, medium or low, with where it is and how it was confirmed. No issues worth fixing, no charge. If the Launch Check finds nothing rated medium or higher, Simon refunds the full $450. You keep the report either way. This applies to the first 10 Launch Checks bought after October 30, 2026.

A different error?

Describe it and get a written quote

Send the error and what you were doing. You get a written scope, a fixed price and a timeline; Simon replies by email, usually within one business day.