Blog
4 min read

TypeScript "Type X Is Not Assignable to Type Y": How to Read and Fix It

TypeScript's most common error means a value doesn't match the type expected where you put it. How to read the message (including long nested ones), the common cases — string vs number, null and undefined, string literals, missing object properties, array types — and fixes that don't just hide the bug.

Type 'string' is not assignable to type 'number'.

This is TypeScript's most common error. It means: you put a value somewhere that expects a different type. The first type is what you gave; the second is what was expected. (What is TypeScript?)

Reading the message

Read it as: "[what you have] is not assignable to [what's required]".

Long errors are nested — each indented line explains why the line above failed. The last line is usually the real problem:

Type '{ name: string; age: string; }' is not assignable to type 'User'.
  Types of property 'age' are incompatible.
    Type 'string' is not assignable to type 'number'.

Translation: the object's age is a string, but User.age must be a number.

Common case 1: string vs number

Values from forms, URLs and localStorage are always strings:

const age: number = event.target.value      // ❌ string
const age: number = Number(event.target.value)  // ✅

Same for query parameters and environment variables. Convert — and validate — at the boundary. (Query parameters explained, Validating input with Zod)

Common case 2: undefined or null

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

The value might be missing. TypeScript wants you to handle that case:

const name: string = user.nickname ?? 'Anonymous'   // fallback

if (!user.nickname) return                          // early exit
useName(user.nickname)                              // now it's string

(Object is possibly undefined)

Environment variables are typed string | undefined because they might not be set:

const key = process.env.API_KEY
if (!key) throw new Error('API_KEY is not set')

Common case 3: string literal types

Type 'string' is not assignable to type '"small" | "medium" | "large"'.

The target only accepts specific strings, but TypeScript sees your value as any string:

let size = 'small'                 // inferred as string
<Button size={size} />             // ❌

const size = 'small'               // inferred as "small"  ✅
let size: 'small' | 'large' = 'small'  // ✅

For objects, as const keeps literal types:

const config = { size: 'small' } as const

Common case 4: object shapes

Property 'email' is missing in type '{ name: string; }' but required in type 'User'.

You're creating or passing an object without a required field. Add it, or make the field optional in the type (email?: string) if it's genuinely optional.

And the reverse — "Object literal may only specify known properties" — means you added a property the type doesn't have, often a typo.

Common case 5: arrays

Type 'never[]' is not assignable to type 'User[]'.

An empty array with no type is inferred as never[] in some situations. Give it a type:

const [users, setUsers] = useState<User[]>([])

(TypeScript generics)

Common case 6: function types

Passing a function whose parameters don't match what's expected — e.g. an onChange handler typed for the wrong event:

const handle = (e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)

Hover over the prop in your editor to see the expected type.

Fixes to avoid

  • as any — switches checking off. The bug is still there; you just won't hear about it.
  • as User on data you haven't checked — tells TypeScript "trust me" about data that may not match. Validate API responses instead.
  • // @ts-ignore — same problem.
  • ! (non-null assertion) — fine when you truly know, but it crashes at runtime when you're wrong.

These are especially tempting for AI tools trying to make errors disappear. If an AI fix adds as any, ask it to fix the actual type instead. (Why TypeScript makes AI-generated code safer)

Tips

  • Hover over variables in VS Code to see their inferred types.
  • Read from the bottom of nested errors.
  • Fix types where data enters your app, and the rest of the code becomes easier.

EasySpawn runs your TypeScript project on a server with Claude Code built in — it can run tsc, read these errors and fix the types properly rather than casting them away. See how it works or join the waitlist.

Related: JavaScript vs TypeScript · Property Does Not Exist on Type · Object Is Possibly Undefined · Why TypeScript Makes AI-Generated Code Safer

Keep reading