TypeScript TS2345: Argument of type 'X' is not assignable to parameter of type 'Y'
Argument not assignable to parameter
Verified against TypeScript docs: Compiler Errors, TypeScript handbook: Type Compatibility, TypeScript 5.5 release notes · Updated June 2026
> quick_fix
TypeScript checked your function call and the argument's type doesn't match the parameter's declared type. Read the error to see which types conflict. Fix by narrowing the argument with a type guard, providing a default value, or updating the function signature to accept the broader type.
// TS2345: Argument of type 'string | undefined' is not assignable
// to parameter of type 'string'
function greet(name: string) { return `Hello, ${name}` }
const input: string | undefined = getInput()
greet(input) // Error
// Fix: narrow first
if (input) {
greet(input) // OK
}
// Or provide a default
greet(input ?? 'Guest') // OKWhat causes this error
TS2345 fires at function call sites when the type of an argument expression is not assignable to the type declared for the corresponding parameter. TypeScript performs the same structural assignability check as TS2322, but TS2345 specifically targets call expressions. The error names both the argument type and the expected parameter type.
How to fix it
- 01
step 1
Read the error message to identify the mismatch
The error states the argument type and the expected parameter type. For object types, it may include a nested 'Types of property X are incompatible' chain that points directly at the diverging property.
Argument of type '{ id: number; }' is not assignable to parameter of type 'User'. Property 'name' is missing in type '{ id: number; }' but required in type 'User'. - 02
step 2
Narrow union types before passing
The most common TS2345 trigger is passing a value that includes null or undefined to a function that expects a non-nullable type. Narrow with an if-check, nullish coalescing, or a non-null assertion (use sparingly).
function process(id: number) { /* ... */ } const userId: number | null = getCurrentUser()?.id ?? null // TS2345: 'number | null' not assignable to 'number' process(userId) // Fix 1: guard if (userId !== null) { process(userId) // userId is number } // Fix 2: default process(userId ?? 0) - 03
step 3
Check callback parameter types
Array methods like map, filter, and reduce expect callbacks with specific signatures. Passing a function with incompatible parameter or return types triggers TS2345.
// TS2345: Argument of type '(x: string) => string' is not // assignable to parameter of type '(value: number) => string' const nums = [1, 2, 3] nums.map((x: string) => x.toUpperCase()) // Fix: match the array's element type nums.map((x: number) => String(x)) - 04
step 4
Handle generic type parameter mismatches
Generic functions infer type parameters from arguments. If two arguments constrain the same type parameter differently, TypeScript can't unify them and emits TS2345. Explicitly specify the type parameter or widen one argument.
function merge<T>(a: T, b: T): T { return { ...a, ...b } } // TS2345: Argument of type '{ y: number }' not assignable // to parameter of type '{ x: number }' merge({ x: 1 }, { y: 2 }) // Fix: explicit type parameter merge<{ x?: number; y?: number }>({ x: 1 }, { y: 2 }) - 05
step 5
Validate external data before passing to typed functions
Data from JSON.parse, fetch responses, or form inputs is typically unknown or any. Validate it with a runtime check or schema library before passing to functions with strict parameter types.
import { z } from 'zod' const UserSchema = z.object({ id: z.number(), name: z.string() }) type User = z.infer<typeof UserSchema> function saveUser(user: User) { /* ... */ } const raw: unknown = await response.json() const user = UserSchema.parse(raw) // throws if invalid saveUser(user) // OK: user is User
How to verify the fix
- Run tsc --noEmit and confirm TS2345 no longer appears
- Check that narrowed branches handle the null/undefined case correctly
- Verify callback return types match what the calling method expects
Why TS2345 happens at the runtime level
TS2345 is emitted by the checker's resolveCall path in src/compiler/checker.ts. When TypeScript resolves a function call, it walks each argument position and checks whether the argument expression's type is assignable to the parameter's declared type using the same structural assignability algorithm as TS2322. For generic functions, it first infers type parameters from arguments, then checks assignability with the inferred types substituted. If the check fails at any argument position, the compiler emits TS2345 pointing at the specific argument that failed.
Common debug mistakes for TS2345
- Passing a value that might be null or undefined to a function that expects a non-nullable type.
- Using point-free style (arr.map(parseInt)) where the callback signature doesn't match the method's expected signature.
- Passing an object literal missing required properties that the parameter type demands.
- Assuming a generic function will infer a union type when two arguments have different shapes.
- Casting with 'as' instead of properly narrowing the argument type before the call.
When TS2345 signals a deeper problem
Persistent TS2345 errors across a codebase reveal a gap between data sources and typed function boundaries. When functions declare strict parameter types but callers receive loosely-typed data from APIs, databases, or user input, every call site becomes a friction point. The systemic fix is to validate data at ingestion boundaries using runtime schemas (zod, valibot, arktype) that produce typed outputs, then pass those validated values through the call chain. Without boundary validation, engineers either litter code with type assertions or widen parameter types until they're meaningless — both defeat the purpose of the type system.
Editor's take
TS2345 is TypeScript's way of telling you the contract between caller and callee is broken. It fires at the exact moment you try to hand data to a function, and the data's shape doesn't match what the function promised to accept. In a well-typed codebase, this is a genuine bug report from the compiler — you're about to pass something wrong. In a poorly-typed codebase, it's noise from accumulated type debt.
The error is closely related to TS2322 (type not assignable) — they share the same structural checking engine under the hood. The difference is purely positional: TS2322 fires on assignments and return statements, TS2345 fires at call sites. In practice, fixing one often surfaces the other because the same data flows through both contexts. A function that returns a union type triggers TS2322 in the function body, and when the return value is passed to another function, TS2345 fires at that call site.
The most insidious TS2345 pattern involves generic functions. When TypeScript infers type parameters from multiple arguments, it tries to unify them into a single type. If two arguments push the inference in different directions, the unification fails and TS2345 fires on whichever argument TypeScript checked second. The error message in this case is often confusing because it references the inferred type parameter rather than the concrete types you see in your code.
Point-free programming is another classic trigger. Writing array.map(parseInt) or array.forEach(console.log) looks clean but the callback signatures rarely match. TypeScript catches this, and it's right to — parseInt('3', 1) returns NaN because 1 isn't a valid radix. TS2345 is preventing a real runtime bug here, not just being pedantic about types.
By Bikram Nath · Curator · Updated June 2026
Frequently asked questions
How does TS2345 differ from TS2322?
Both check structural type assignability, but they fire in different contexts. TS2322 fires on assignments (variable declarations, return statements, property assignments). TS2345 fires specifically at function call sites when an argument doesn't match a parameter type. The fix patterns are identical: narrow the source type, widen the target type, or add missing properties. In practice, you often fix one and surface the other because the same data flows through assignments and function calls.
Why does passing parseInt to Array.map cause TS2345?
Array.map passes three arguments to its callback: element, index, and array. parseInt accepts (string, radix?). When you write ['1','2'].map(parseInt), TypeScript sees that map's callback signature (value: string, index: number, array: string[]) doesn't match parseInt(string, number | undefined). The index gets passed as the radix, which is also a runtime bug. Fix by wrapping: ['1','2'].map(s => parseInt(s, 10)). This is one case where TS2345 catches a real runtime bug.
Should I use 'as' to fix TS2345?
No. Using 'as' on a function argument tells TypeScript to trust your assertion, but if the argument genuinely doesn't match the parameter type, you'll crash at runtime. The proper fix is to narrow the argument type (type guard, nullish coalescing, validation) so TypeScript can verify correctness. If you find yourself needing 'as' at call sites frequently, the function's parameter type is probably too narrow or your data lacks proper validation at the boundary.