Fix: TypeScript Argument of type 'X' is not assignable to parameter of type 'Y' (TS2345)
Quick Answer
How to fix TypeScript error TS2345 Argument of type is not assignable to parameter of type, covering null narrowing, union types, generics, callback types, type widening, enums, and React event handlers.
The Error
You call a function in TypeScript and get:
error TS2345: Argument of type 'string | undefined' is not assignable to parameter of type 'string'.
Type 'undefined' is not assignable to type 'string'.Or one of its many variants:
error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.error TS2345: Argument of type '{ name: string; }' is not assignable to parameter of type 'User'.
Property 'email' is missing in type '{ name: string; }' but required in type 'User'.error TS2345: Argument of type 'string' is not assignable to parameter of type '"success" | "error"'.TS2345 means the value you are passing to a function does not match what the function expects. The types are incompatible. TypeScript is preventing a potential runtime bug before your code even runs.
Why This Happens
TypeScript checks that every function argument matches the parameter type declared in the function signature. When the types don’t align, TypeScript blocks the call.
This happens because:
- The value might be null or undefined. A variable typed as
string | undefinedcannot be passed where astringis expected. - A union type is too wide. You pass a general
stringwhere a specific literal type like"success" | "error"is required. - An object is missing properties. The argument has fewer fields than the parameter type requires.
- Type widening. A
letvariable widens its type, making it incompatible with a narrow parameter type. - Generic type constraints don’t match between the caller and the function.
- Callback signatures have different parameter types than expected.
The fix depends on which mismatch you have. Each scenario is covered below.
Fix 1: Narrow null and undefined with Type Guards
The most common TS2345 scenario. A value might be undefined or null, but the function requires a definite value:
function greet(name: string) {
console.log(`Hello, ${name}`);
}
const user = { name: "Alice" } as { name?: string };
greet(user.name); // Error: Argument of type 'string | undefined' is not assignable to parameter of type 'string'Fix: Add a null check before calling:
if (user.name) {
greet(user.name); // Works — TypeScript narrows to string
}Or use the nullish coalescing operator for a default value:
greet(user.name ?? "Guest"); // Works — always a stringOr use the non-null assertion operator if you are certain the value exists:
greet(user.name!); // Works — but risky if name is actually undefinedWarning: The ! operator tells TypeScript to trust you. If the value is actually undefined at runtime, you get a runtime error. Use it only when you have external knowledge that TypeScript cannot infer. For more on handling possibly undefined values, see fixing “Object is possibly undefined”.
Fix 2: Use Literal Types Correctly (const vs let)
TypeScript widens let variables to their base type. This causes TS2345 when a function expects a narrow literal type:
type Status = "success" | "error" | "pending";
function setStatus(status: Status) { /* ... */ }
let s = "success";
setStatus(s); // Error: Argument of type 'string' is not assignable to parameter of type 'Status'let s = "success" gives s the type string (widened), not "success" (literal). The fix:
Option 1: Use const:
const s = "success"; // Type is "success" (literal)
setStatus(s); // WorksOption 2: Use as const:
let s = "success" as const; // Type is "success"
setStatus(s); // WorksOption 3: Annotate the type explicitly:
let s: Status = "success";
setStatus(s); // WorksPro Tip: Prefer
constfor values that don’t change. It gives TypeScript the narrowest possible type and prevents accidental reassignment. If you needletfor a value that changes between specific options, annotate it with the union type explicitly.
This same widening issue affects objects:
const config = { mode: "production" }; // mode is string, not "production"
setMode(config.mode); // Error if setMode expects "production" | "development"
// Fix: use as const on the object
const config = { mode: "production" } as const; // mode is "production"Fix 3: Add Missing Object Properties
When passing an object literal, TypeScript checks that it has all required properties:
interface User {
name: string;
email: string;
age: number;
}
function createUser(user: User) { /* ... */ }
createUser({ name: "Alice" });
// Error: Argument of type '{ name: string; }' is not assignable to parameter of type 'User'.
// Property 'email' is missingFix: Include all required properties:
createUser({ name: "Alice", email: "alice@example.com", age: 30 });If some properties should be optional, update the interface:
interface User {
name: string;
email: string;
age?: number; // Now optional
}Or use the Partial utility type when all properties should be optional:
function updateUser(updates: Partial<User>) { /* ... */ }
updateUser({ name: "Alice" }); // Works — all fields optionalIf the type mismatch is between two similar but different types, see fixing “Type is not assignable to type” for a deeper dive.
Fix 4: Handle Union Type Mismatches
When a function accepts a union type but you pass a value that doesn’t match any member:
function processEvent(event: MouseEvent | KeyboardEvent) { /* ... */ }
const e = new Event("click");
processEvent(e); // Error: Argument of type 'Event' is not assignableEvent is the base class. MouseEvent and KeyboardEvent are subclasses. TypeScript requires the specific subtype.
Fix: Use the correct event type:
const e = new MouseEvent("click");
processEvent(e); // WorksOr widen the function signature if it should accept any event:
function processEvent(event: Event) { /* ... */ }For union types with literal members, make sure your value is one of the allowed options:
type Direction = "up" | "down" | "left" | "right";
function move(dir: Direction) { /* ... */ }
const userInput: string = getUserInput();
move(userInput); // Error: 'string' is not assignable to 'Direction'
// Fix: validate first
if (userInput === "up" || userInput === "down" || userInput === "left" || userInput === "right") {
move(userInput); // Works — TypeScript narrows to Direction
}Fix 5: Fix Generic Type Constraints
Generic functions have type parameters that must satisfy constraints. If the argument doesn’t match the constraint, you get TS2345:
function merge<T extends object>(target: T, source: Partial<T>): T {
return { ...target, ...source };
}
merge("hello", {}); // Error: Argument of type 'string' is not assignable to parameter of type 'object'The constraint T extends object means T must be an object, not a primitive.
Fix: Pass the correct type:
merge({ name: "Alice" }, { name: "Bob" }); // WorksIf you control the generic function and need to accept more types, loosen the constraint:
function merge<T>(target: T, source: Partial<T>): T { /* ... */ }Fix 6: Fix Callback and Function Parameter Types
When passing a callback, the callback’s parameter types must match what the caller expects:
function fetchData(callback: (data: string) => void) {
callback("result");
}
fetchData((data: number) => {
console.log(data);
});
// Error: Argument of type '(data: number) => void' is not assignableFix: Match the callback signature:
fetchData((data: string) => {
console.log(data);
}); // WorksOr let TypeScript infer the type:
fetchData((data) => {
console.log(data); // data is inferred as string
});Array methods like .map(), .filter(), and .reduce() often trigger this when the callback return type doesn’t match expectations:
const nums = [1, 2, 3];
const strs: string[] = nums.map((n) => n); // Error: number is not assignable to string
// Fix
const strs: string[] = nums.map((n) => String(n));Fix 7: Handle React Event Handler Types
React has its own event type system. Passing the wrong event type to a handler is a frequent source of TS2345:
function handleClick(e: React.MouseEvent<HTMLButtonElement>) {
console.log(e.currentTarget.value);
}
// Error if you pass this to an onChange handler (which expects ChangeEvent)
<input onChange={handleClick} />Fix: Use the correct React event type:
function handleChange(e: React.ChangeEvent<HTMLInputElement>) {
console.log(e.target.value);
}
<input onChange={handleChange} /> // WorksCommon React event types:
| Event | Type |
|---|---|
onClick | React.MouseEvent<HTMLElement> |
onChange | React.ChangeEvent<HTMLInputElement> |
onSubmit | React.FormEvent<HTMLFormElement> |
onKeyDown | React.KeyboardEvent<HTMLElement> |
onFocus | React.FocusEvent<HTMLElement> |
If the event target type is causing issues, you may also encounter Property does not exist on type errors when accessing properties like value or dataset on the event target.
Fix 8: Use Enum Values Correctly
Enums create their own type. You cannot pass a raw string or number where an enum is expected:
enum Color {
Red = "RED",
Green = "GREEN",
Blue = "BLUE",
}
function paint(color: Color) { /* ... */ }
paint("RED"); // Error: Argument of type '"RED"' is not assignable to parameter of type 'Color'Fix: Use the enum member:
paint(Color.Red); // WorksIf you receive strings from external data (APIs, user input) and need to convert:
function toColor(value: string): Color {
if (Object.values(Color).includes(value as Color)) {
return value as Color;
}
throw new Error(`Invalid color: ${value}`);
}
paint(toColor("RED")); // Works with validationCommon Mistake: Using
as Colorwithout validation. If the string doesn’t match any enum value, you silently pass an invalid value. Always validate external data before casting to an enum.
Fix 9: Use Utility Types (Pick, Omit, Required)
TypeScript’s utility types help reshape types to match function signatures:
interface FullUser {
id: number;
name: string;
email: string;
role: string;
}
// Function only needs some fields
function sendEmail(user: Pick<FullUser, "name" | "email">) { /* ... */ }
const fullUser: FullUser = { id: 1, name: "Alice", email: "a@b.com", role: "admin" };
sendEmail(fullUser); // Works — FullUser has name and emailUseful utility types for fixing TS2345:
| Utility | What it does |
|---|---|
Partial<T> | Makes all properties optional |
Required<T> | Makes all properties required |
Pick<T, K> | Selects specific properties |
Omit<T, K> | Removes specific properties |
Record<K, V> | Creates a type with keys K and values V |
Readonly<T> | Makes all properties readonly |
Fix 10: Handle string vs String (Primitive vs Wrapper)
TypeScript distinguishes between primitive types (lowercase) and their wrapper objects (uppercase):
function greet(name: string) { /* ... */ }
const s: String = new String("Alice");
greet(s); // Error: Argument of type 'String' is not assignable to parameter of type 'string'.
// 'string' is a primitive, but 'String' is a wrapper object.Fix: Always use lowercase primitive types:
const s: string = "Alice";
greet(s); // WorksNever use String, Number, Boolean, or Object as types. Always use string, number, boolean, and object. The uppercase versions are JavaScript wrapper objects that behave differently from primitives. If your module types aren’t being found at all, see fixing “Cannot find module”.
Fix 11: Type Assertions as a Last Resort
If you are certain the types are compatible but TypeScript disagrees, a type assertion can unblock you:
function processId(id: number) { /* ... */ }
const value: unknown = JSON.parse('42');
processId(value as number); // Works — you assert it's a numberFor cases where the types are completely unrelated, you need a double assertion:
processId(value as unknown as number);Warning: Assertions override TypeScript’s type checker. If the runtime value doesn’t match the asserted type, you get a runtime error. Use assertions sparingly and only when you have external knowledge that TypeScript cannot verify. Prefer type guards (Fix 1) whenever possible. For related ESLint issues around type safety, check fixing ESLint parsing errors.
Still Not Working?
If none of the fixes above resolved your TS2345 error:
Check for mismatched dependency types. If a function comes from a library and its type definitions are outdated, the types might not match your code. Update the types:
npm install @types/some-library@latestCheck for conflicting TypeScript versions. Different packages might depend on different TypeScript versions. Check with:
npm ls typescriptMultiple versions can cause type incompatibilities.
Restart the TypeScript language server. In VS Code, press Ctrl+Shift+P and run “TypeScript: Restart TS Server.” Stale caches sometimes show phantom errors.
Check your tsconfig.json strict settings. The strict flag enables several sub-options (strictNullChecks, strictFunctionTypes, etc.) that increase the number of TS2345 errors. Don’t disable strict mode — fix the errors instead.
Use the TypeScript Playground. Paste a minimal reproduction into the TypeScript Playground to experiment with type structures in isolation. This helps isolate whether the issue is in your types or your tooling.
Read the full error message. TS2345 errors often include a chain of “Type A is not assignable to Type B” messages. Read the entire chain — the last line usually pinpoints the exact property or type that mismatches. If the root issue is a broader type assignment problem, see fixing “Type is not assignable to type” for comprehensive solutions.
Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.
Was this article helpful?
Related Articles
Fix: TypeError: x is not a function
How to fix JavaScript TypeError is not a function caused by wrong variable types, missing imports, overwritten variables, incorrect method names, and callback issues.
Fix: TypeScript Could not find a declaration file for module (TS7016)
How to fix the TypeScript TS7016 error 'Could not find a declaration file for module' by installing @types packages, creating declaration files, and configuring tsconfig.json.
Fix: TypeScript Property does not exist on type (TS2339)
How to fix TypeScript error TS2339 'Property does not exist on type'. Covers missing interface properties, type narrowing, optional chaining, intersection types, index signatures, type assertions, type guards, window augmentation, and discriminated unions.
Fix: TypeScript Type 'X | undefined' is not assignable to type 'X'
How to fix TypeScript strict null checks error Type X undefined is not assignable caused by optional values, nullable types, missing guards, and strictNullChecks.