Quick answer
It's TS2322's twin for function calls: the argument's type doesn't match the parameter. Make them match — fix the value, fix the parameter type, or narrow a | undefined/| null argument before the call. For string-literal unions, type the source or use as const.
The exact error string
function greet(name: string) { return "Hi " + name; }
greet(42);
// error TS2345: Argument of type 'number' is not assignable to
// parameter of type 'string'.
let maybe: string | undefined;
greet(maybe);
// error TS2345: Argument of type 'string | undefined' is not assignable
// to parameter of type 'string'.
How to diagnose it
Read the two types in the message, then ask which side is wrong — and read down to any nested Types of property '...' are incompatible line for object arguments, just like TS2322. The shape of the argument type tells you the fix:
- a plain mismatch (
numbervsstring) → convert the value or fix the parameter. ... | undefined/| null→ narrow at the call site.- a wide
stringvs a literal union → type the source /as const. - an object with a deep nested mismatch → the innermost line names the offending field.
Fix 1: match the value to the parameter
greet(String(42)); // convert the value
greet("Ada"); // or pass the right type
Fix 2: narrow undefined/null before the call
greet(maybe ?? "there"); // default
if (maybe) greet(maybe); // guard narrows to string
// or, if undefined is genuinely valid, widen the parameter:
function greet(name?: string) { /* ... */ }
Fix 3: string-literal unions
function sort(order: "asc" | "desc") { /* ... */ }
let dir = "asc"; // inferred as string
sort(dir); // ❌ TS2345
const dir2 = "asc"; // inferred as "asc"
sort(dir2); // ✅
let dir3: "asc" | "desc" = "asc";
sort(dir3); // ✅
Worked example: passing parsed JSON to a typed function
You fetch data, type it, and pass it to a helper whose parameter expects a slightly different shape — TS2345 names the field that doesn't line up:
interface ApiUser { id: string; name: string } // API sends id as a string
function saveUser(u: { id: number; name: string }) { /* ... */ }
const user: ApiUser = await (await fetch("/api/user")).json();
saveUser(user);
// error TS2345: Argument of type 'ApiUser' is not assignable to parameter ...
// Types of property 'id' are incompatible: 'string' is not assignable to 'number'.
saveUser({ ...user, id: Number(user.id) }); // ✅ convert at the boundary
Generate ApiUser from a real sample with JSON to TypeScript so the mismatch is visible at the call, not hidden behind any.
TS2345 in React
const [count, setCount] = useState(0);
// functional updater must return the same type T:
setCount(prev => prev + "1"); // ❌ string not assignable to parameter (number)
setCount(prev => prev + 1); // ✅
// passing an event handler with the wrong signature:
function onClick(id: number) {}
<button onClick={onClick} /> // ❌ MouseEvent arg not assignable to (id: number)
<button onClick={() => onClick(1)} /> // ✅ adapt the signature
Common variants of this message
| Variant | Typical cause | Fix |
|---|---|---|
'number' is not assignable to parameter of type 'string' | wrong primitive | convert / fix the value |
'string | undefined' is not assignable to parameter of type 'string' | optional / find() result | narrow or ?? |
'string' is not assignable to parameter of type '"asc" | "desc"' | widened literal | as const / type the source |
Argument ... Types of property 'x' are incompatible | object field mismatch | fix that field / convert |
No overload matches this call | wrong count/types vs overloads | match a declared signature |
Debugging checklist
- ✓ Read the argument type vs the parameter type the error names
- ✓ Fix the value, or fix the parameter's declared type
- ✓
| undefined/| nullargument? Narrow with a guard or?? - ✓ Literal union parameter? Type the source or use
as const - ✓ Object argument? Read the nested message for the offending property
- ✓ React updater/handler? Return a full
T/ adapt the signature - ✓ Argument is parsed JSON? Type it via JSON to TypeScript and convert
Frequently Asked Questions
What is the difference between TS2345 and TS2322?
Same kind of mismatch, different place. TS2322 fires on an assignment (const x: T = value); TS2345 fires on a function call (fn(value)) when the argument's type isn't assignable to the parameter. Both are fixed by aligning the value's type with the target.
Why does the argument count also matter?
If you pass too many or too few arguments you get a different error (TS2554), but with overloads a wrong count surfaces as 'no overload matches this call' alongside TS2345. Check the argument count against the signature first, then the types.
Why does undefined sneak in from a default or optional?
An optional property or a value from find()/get()/an optional param is T | undefined, and passing it to a parameter typed plain T triggers TS2345. Narrow it (guard or ?? default) at the call site, or make the parameter accept undefined if that is valid.
How do I fix it on a React setState updater?
setState's functional form expects (prev: T) => T. Returning a different shape, or returning undefined from a branch, makes the updater argument not assignable. Ensure every path returns a full T, and type the state correctly with useState<T>().
Why does 'string' fail against a literal union parameter?
A parameter like 'asc' | 'desc' accepts only those literals, but a let-declared string is widened to string. Use a const, annotate the variable as the union, or add as const so the value keeps its literal type.
The argument is parsed JSON typed as any — why does it still fail?
any is assignable to anything, so a true any argument won't trigger TS2345 — but once you (correctly) type the parsed value, a real mismatch with the function's parameter appears. That is the type system surfacing a genuine shape difference; generate the interface from a real sample and convert as needed.
Generate accurate types from your JSON
Turn a real API response into a TypeScript interface so your function calls are properly type-checked — nothing is uploaded to a server.