error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. is reported at a call when an argument does not fit the type the function's @param tag declares. It is the call-site twin of TS2322: same comparison, reported on the argument.
In JavaScript code two cases account for most of these errors: text from the DOM (an input's value is always a string) passed where a number is expected, and an optional property passed on as if it were always set. The second only appears with strictNullChecks, which strict turns on.
At a glance
| Code | TS2345 |
|---|---|
| Message | Argument of type '{0}' is not assignable to parameter of type '{1}'. |
| TypeScript's category | error |
| Listed under | Types do not match |
| Turned on by | No option: every checked file reports it |
| In .js files | Reported when the file is checked: checkJs in jsconfig.json, or // @ts-check at the top of the file |
The message is TypeScript's own text from its diagnostic messages file (Apache-2.0); {0} and {1} are filled in by tsc for each error.
What it looks like
/** @param {number} cents */
export function formatPrice(cents) {
return `$${(cents / 100).toFixed(2)}`;
}
const field = document.querySelector("input");
if (field) {
console.log(formatPrice(field.value));error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'.
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} /** @typedef {{ id: string, discountCents?: number }} Order */
/** @param {number} cents */
export function formatPrice(cents) {
return `$${(cents / 100).toFixed(2)}`;
}
/** @param {Order} order */
export function discountLabel(order) {
return `You save ${formatPrice(order.discountCents)}`;error TS2345: Argument of type 'number | undefined' is not assignable to parameter of type 'number'.
Type 'undefined' is not assignable to type 'number'.
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} What causes it
- The function's
@paramtag declares a type and the argument has another one. - The argument may be
undefinedornull(an optional property, the result offind, a DOM lookup) and the parameter's type does not include it. The message then ends withType 'undefined' is not assignable to type 'number'.
How to fix it
Convert or check the argument at the call
Turn form text into a number with Number(field.value), or read field.valueAsNumber on a number input. For a value that may be missing, check it first; after if (order.discountCents === undefined) return ... the checker knows the property is a number on the next line.
/** @typedef {{ id: string, discountCents?: number }} Order */
/** @param {number} cents */
export function formatPrice(cents) {
return `$${(cents / 100).toFixed(2)}`;
}
const field = document.querySelector("input");
if (field) {
console.log(formatPrice(Number(field.value)));
console.log(formatPrice(field.valueAsNumber));
}
/** @param {Order} order */
export function discountLabel(order) {
if (order.discountCents === undefined) return "";
return `You save ${formatPrice(order.discountCents)}`;
}jsconfig.json for this example
{
"compilerOptions": {
"checkJs": true,
"noEmit": true,
"strict": true,
"target": "es2022",
"module": "nodenext",
"types": []
},
"include": ["*.js"]
} Or let the function accept more
If the function can handle the other type, say so in its JSDoc: @param {number | undefined} cents, or an optional parameter @param {number} [cents]. Then handle that case inside the function, where it is written once instead of at every call.
Arguments that are functions
When the argument is a callback, the message compares function types and the indented lines say which parameter or return type differs. Fix the callback's @param tags, or drop them and let the parameter's type supply them: a function passed directly gets its parameter types from the context.
Gotchas
- An object literal argument with a property the parameter's type does not have is reported as TS2353, not TS2345.
- If the function has several call signatures (overloads, or a built-in such as
new Date()), a mismatch is reported as TS2769 and lists each signature. parseInt(field.value)also converts text, but an empty field givesNaNfromparseIntand0fromNumber; the checker only seesnumbereither way.
Questions
The value is a number when the code runs. Why does tsc say string?
The checker goes by declared types, not by values at run time. HTMLInputElement.value is declared as string in TypeScript's DOM types even for <input type="number">; valueAsNumber is the property declared as number.
Sources
- More on Functions · TypeScript handbook
- Narrowing · TypeScript handbook
- HTMLInputElement: valueAsNumber property · MDN Web Docs
- diagnosticMessages.json (TypeScript 5.9.3) · TypeScript on GitHub
Last reviewed 20 October 2026.