Fix TypeScript Error: Argument of type 'string' is not assignable to parameter of type 'number' (ts2345)

beginner๐Ÿ”ต TypeScript2026-04-16| TypeScript 4.x / 5.x, Node.js, any OS (Windows / macOS / Linux), VS Code or any TypeScript-aware editor

Error Message

Argument of type 'string' is not assignable to parameter of type 'number'. ts(2345)
#typescript#argument#parameter#type

The Error

Argument of type 'string' is not assignable to parameter of type 'number'. ts(2345)

You passed a value to a function, and TypeScript rejected it because the types don't line up. Nine times out of ten, you've got a string where the function wants a number โ€” but the same mismatch appears with boolean vs string, null vs object, and plenty of other combinations.

Good news: this is a compile-time error. TypeScript caught the bug before it reached production.

Why This Happens

TypeScript checks every function call against its declared parameter types. Pass a number param a string โ€” even "42" โ€” and the compiler refuses. It doesn't care that the value looks numeric.

The usual culprits:

  • Reading from process.argv, req.query, or URLSearchParams โ€” these always return strings, even when the value is "42".
  • Grabbing a value from an HTML <input> โ€” input.value is always string, no exceptions.
  • Parsing JSON where a field arrived as "10" instead of 10.
  • A variable that started as number but got widened to string | number somewhere upstream.
  • Passing arguments to a library function in the wrong order.

Step-by-Step Fix

Step 1 โ€” Find the mismatch

TypeScript tells you the exact line. Open it, look at what you're passing, then check the function signature. The gap is usually obvious:

// ts(2345)
function double(n: number): number {
  return n * 2;
}

const input = "5"; // type: string
console.log(double(input)); // โŒ string โ†’ number mismatch

Step 2 โ€” Convert the value

A string that represents a number needs to be parsed before you pass it in.

const input = "5";
console.log(double(Number(input)));       // โœ… explicit, handles decimals
console.log(double(parseInt(input, 10))); // โœ… better for integers โ€” always pass radix 10
console.log(double(+input));              // โœ… concise, but easy to miss in code reviews

Use Number() when the value might be a decimal. Use parseInt(input, 10) when you specifically need an integer. Skip the unary + in team codebases โ€” it's a pain to grep and looks like a typo at a glance.

Step 3 โ€” Handle the common sources

CLI args via process.argv:

const raw = process.argv[2]; // string | undefined
const port = Number(raw);
if (isNaN(port)) throw new Error(`Invalid port: ${raw}`);
startServer(port); // โœ…

HTML input fields:

const input = document.getElementById("age") as HTMLInputElement;
const age = Number(input.value);
if (isNaN(age)) return; // catches empty string and "abc"
setAge(age); // โœ…

URL query params (Express / Fetch API):

// Express
app.get("/users", (req, res) => {
  const page = Number(req.query.page); // req.query.page is string | string[] | ParsedQs
  getUsers(page); // โœ…
});

// URLSearchParams
const params = new URLSearchParams(location.search);
const id = Number(params.get("id")); // get() returns string | null
if (isNaN(id)) throw new Error("Missing id param");
fetchItem(id); // โœ…

JSON payloads with stringly-typed numbers:

// Server returned { "count": "10" } instead of { "count": 10 }
const data = JSON.parse(response) as { count: string };
const count = Number(data.count);
renderList(count); // โœ…

Step 4 โ€” Narrow a union type

TypeScript sometimes widens a variable to string | number. A function that only accepts number will still reject it. Use a type guard to narrow before passing.

function applyDiscount(price: number): number {
  return price * 0.9;
}

const rawPrice: string | number = getPrice();

// Option A: narrow with typeof
if (typeof rawPrice === "number") {
  applyDiscount(rawPrice); // โœ… TypeScript knows it's number here
}

// Option B: convert unconditionally
applyDiscount(Number(rawPrice)); // โœ… works either way

Step 5 โ€” Fix the function signature instead

Sometimes the call site is fine and the signature is the problem. If a function is genuinely designed to handle both strings and numbers, update its parameter type to reflect that.

// Before โ€” too strict
function log(value: number): void {
  console.log(value);
}

// After โ€” accepts both
function log(value: number | string): void {
  console.log(value);
}

Only widen the type if the function actually handles both. Don't do it just to silence the error โ€” that defeats the whole point of TypeScript.

Verify the Fix

Run the compiler to confirm nothing remains:

# Full project check
npx tsc --noEmit

# Single file
npx tsc --noEmit src/yourfile.ts

# With ts-node
npx ts-node src/yourfile.ts

No output means zero type errors. In VS Code, the red squiggly disappears the moment you save the file.

Quick Reference

  • Number(x) โ€” converts string/boolean/null to number; returns NaN if unparseable
  • parseInt(x, 10) โ€” integer from string; silently drops trailing non-numeric chars ("42px" โ†’ 42)
  • parseFloat(x) โ€” decimal from string; useful for "3.14"
  • +x โ€” shorthand for Number(x); avoid in shared codebases, it's hard to grep
  • Always validate with isNaN() when the input comes from users or external sources

When Conversion Is the Wrong Fix

Seeing ts(2345) in a dozen places? Before scattering Number() calls across your codebase, check your data model. UUIDs and MongoDB ObjectIDs are strings by nature โ€” converting them to numbers at every call site is fighting the wrong battle. Type them as string from the start and the errors vanish for good. Widespread ts(2345) errors are usually a symptom of a type mismatch in your data layer, not a conversion problem at each individual boundary.

Related Error Notes