What you'll learn
Quick Answer
Type 'X' is not assignable to type 'Y'means you supplied a value of typeXwhere aYis required, and anXis not always a validY. Read it as "I have an X; the slot wants a Y." When the message indents further with Types of property '...' are incompatible, follow the indentation - the deepest line is the real mismatch. The most common trigger isundefinedslipping into a union from an array lookup, aMap, or an optional property.
How to Read the Error
The shape is always Type 'A' is not assignable to type 'B'. B is what the position requires - a variable's declared type, a parameter, a return type, an object property. A is what you gave it. Assignability runs one direction: every number is a valid number | undefined, but not every number | undefined is a valid number.
Two error codes carry this. TS2322 is for assignments and object properties. TS2345 is for function arguments and reads Argument of type 'A' is not assignable to parameter of type 'B'. When the types nest, TypeScript prints a stack:
Type '(id: string) => void' is not assignable to type 'Handler'.
Types of parameters 'id' and 'id' are incompatible.
Type 'number' is not assignable to type 'string'.That is real verified output. Read it top to bottom: the headline names the outer types, each indent narrows to the specific incompatible piece, and the last line is what to fix - here a handler declared with string where Handler supplies a number.
The undefined That Sneaks In
Under strict (specifically strictNullChecks), many standard-library calls return T | undefined:
const names: string[] = ["asha", "ravi"];
const found = names.find(n => n === "asha");
greet(found);Verified error:
Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
Type 'undefined' is not assignable to type 'string'.Array.prototype.find, Map.get, indexed access with noUncheckedIndexedAccess, document.querySelector, and optional properties all do this. TypeScript is correct - the item might be missing. Fixes, best first:
- Narrow it:
if (found !== undefined) { greet(found); }- inside the blockfoundisstring. Verified to compile clean. - Default it:
scores.get("ravi") ?? 0producesnumber. Verified clean. - Assert it exists:
if (!found) throw new Error("missing name");
Avoid found!, the non-null assertion, unless you genuinely know more than the compiler - it hides the check without making the value safe, so if the value really is missing you get a runtime crash instead of a compile error. If a value is legitimately optional, consider making that explicit in the type (name?: string) so every caller is forced to handle the absent case.
Literal Widening and Union Typos
type Status = "pending" | "active" | "closed";
let raw = "active";
const s: Status = raw; // Type 'string' is not assignable to type 'Status'.Verified. let raw = "active" is inferred as string, not "active" - TypeScript widens the literal because a let can be reassigned to any string. Switch to const raw = "active" and the type stays "active", which is assignable to Status. For object properties use as const: const cfg = { mode: "dark" } as const makes cfg.mode the type "dark" instead of string. Verified: both compile clean.
The sibling error is a plain typo:
setStatus("actve");
// Argument of type '"actve"' is not assignable to parameter of type 'Status'.This is the union type doing its job - the misspelling is caught at compile time instead of slipping through to a runtime bug where status is silently never "active" and a status check never fires. The same widening happens with numbers and booleans in a let, but string-literal unions are where it bites most often, because that is where the exact spelling carries meaning.
Objects: Wrong, Extra, and Nested
Three distinct object errors show up:
- Wrong property type -
TS2322: Type 'number' is not assignable to type 'string'on{ id: 1, name: 42 }wherenameisstring. - Excess property -
TS2353: Object literal may only specify known properties, and 'verbose' does not exist in type 'Options'. - Nested mismatch - the error drills down through
datatoType 'string' is not assignable to type 'number'oncount. Follow the indentation to the leaf.
The excess-property one has a twist. connect({ timeout: 1000, retries: 3, verbose: true }) is rejected, but assigning that identical object to a variable first and passing the variable is accepted - verified. Structural typing allows extra properties in general; the literal check is a narrow safety net aimed at typos in options objects passed inline.
The Gotcha: as Does Not Convert Anything
const data = JSON.parse('{"id": 1}') as User;
console.log(data.name.toUpperCase());tsc compiles this with zero errors - verified, exit code 0. At runtime it throws TypeError: Cannot read properties of undefined (reading 'toUpperCase') - also verified.
A type assertion (as User, or the older <User>value) tells the compiler "treat this as User" and switches off checking for that value. It does not parse, validate, or coerce anything. When the data does not match the type, the error just relocates from compile time, where it is cheap, to runtime, where your users hit it.
Legitimate uses are narrow: asserting a more specific DOM type after you have checked (e.target as HTMLInputElement), or as const. For anything from outside your code - API responses, JSON.parse, localStorage, form fields - validate instead, with a type-guard function or a schema library such as Zod that checks the shape and returns a typed value. Writing as just to make an error disappear is usually hiding a real bug.
A Quick Checklist
When you hit "not assignable":
- Work out which type is the slot and which is your value - the message reads
Type <yours> is not assignable to type <required>. - If
undefinedornullis in your type, narrow with anif, default with??, or restructure so the value cannot be absent. - If your value is
stringbut the slot wants a union of string literals, changelettoconstor addas const. - If it is a function, check the parameter and return types - the indented lines name the exact one.
- If it is an object literal with an "unknown property" message, you have a misspelled key or a field that belongs somewhere else.
- Reach for
aslast, and only when you can say out loud why you know more than the compiler.
