Fix TypeScript Error: Type 'string' is not assignable to type 'number' (ts2322)

beginner๐Ÿ”ต TypeScript2026-03-19| TypeScript 4.x / 5.x, Node.js, VS Code, any TypeScript project

Error Message

Type 'string' is not assignable to type 'number'. ts(2322)
#typescript#type#assignable#ts2322

What Happened

You're working on a TypeScript project and the compiler โ€” or VS Code's red squiggly โ€” throws this at you:

Type 'string' is not assignable to type 'number'. ts(2322)

That ts(2322) code means a type mismatch: you're handing a string to something that expects a number. It's probably the most common type error you'll encounter in TypeScript โ€” and it's almost always a five-minute fix once you know what to look for.

Common Scenarios

Four situations account for the vast majority of these errors. Recognize them and you'll rarely be stuck.

1. Assigning a string literal to a number variable

let age: number = "25"; // Error: Type 'string' is not assignable to type 'number'

Classic case. The variable wants a number, but you're giving it a string.

2. Function return type mismatch

function getScore(): number {
  return "100"; // Error: Type 'string' is not assignable to type 'number'
}

Same problem, different location โ€” the function promises to return a number but hands back a string.

3. Object property type mismatch

interface User {
  id: number;
  name: string;
}

const user: User = {
  id: "abc123", // Error: Type 'string' is not assignable to type 'number'
  name: "Alice",
};

4. API response or form input (extremely common)

// Form input values are always strings โ€” no exceptions
const input = document.getElementById("age") as HTMLInputElement;
const age: number = input.value; // Error: 'string' not assignable to 'number'

This one bites a lot of developers. The DOM gives you strings. TypeScript expects you to convert them explicitly.

How to Debug It

Start by finding the exact line. In VS Code, hover over the red underline โ€” TypeScript tells you exactly which variable is conflicting and why. Prefer the terminal? Run:

npx tsc --noEmit

This type-checks your entire project without emitting any output files. Every ts(2322) appears with a file path and line number.

Once you've located the problem, three questions help narrow the fix:

  • Should this variable actually be a string or a number?
  • Is the data coming from an external source โ€” an API, a form field, or an environment variable?
  • Did someone recently change an interface or function signature?

Solutions

Fix 1: Convert the string to a number

When the value is a string but you genuinely need a number, convert it explicitly. JavaScript gives you three tools:

// Number() โ€” works for integers and decimals
const age: number = Number(input.value);

// parseInt โ€” strips decimals, always pass radix 10
const count: number = parseInt(input.value, 10);

// parseFloat โ€” keeps decimal places
const price: number = parseFloat(input.value);

One catch: Number("abc") doesn't throw โ€” it quietly returns NaN. That's technically a number in JavaScript, but it'll break any math you try to do with it. Always validate:

const age = Number(input.value);
if (isNaN(age)) {
  throw new Error("Invalid age value");
}

Fix 2: Change the type annotation if string is correct

Sometimes the annotation is the bug, not the value. If the variable should actually hold a string, fix the type:

// Change the type
let id: string = "abc123";

// Or use a union if it can legitimately be either
let id: string | number = "abc123";

Fix 3: Fix the function return type

// Option A: return the right type
function getScore(): number {
  return 100; // actual number, not a string
}

// Option B: update the return type annotation
function getScore(): string {
  return "100";
}

Fix 4: Update the interface to match reality

API responses often have a different shape than you assumed when writing the interface. Fix the interface rather than forcing the data to fit:

interface User {
  id: string; // changed from number โ€” the API actually sends strings
  name: string;
}

const user: User = {
  id: "abc123", // now valid
  name: "Alice",
};

Fix 5: Handle the environment variable case

In Node.js, every environment variable is a string | undefined โ€” no exceptions. So process.env.PORT is never a number, even if you put 3000 in your .env file:

// This errors:
const port: number = process.env.PORT; // Type: string | undefined

// Fix โ€” convert and provide a fallback:
const port: number = Number(process.env.PORT) || 3000;

When the Data Comes from an API

Type assertions like as MyInterface are a trap here. They tell TypeScript to trust you, but they do zero runtime checking. If the API changes its response shape, your code silently breaks:

// Risky โ€” no runtime validation
const data = response.json() as MyInterface;

// Safer โ€” zod validates the shape at runtime and throws early if it's wrong
import { z } from "zod";

const UserSchema = z.object({
  id: z.number(),
  name: z.string(),
});

const user = UserSchema.parse(await response.json());

If the API sends id: "abc123" when you expected a number, zod throws immediately with a clear error message โ€” instead of propagating NaN through your app.

Verifying the Fix

Run the compiler. No output means no errors:

npx tsc --noEmit

The red squiggly in VS Code should disappear as soon as you save. If it lingers, restart the TS language server: open the command palette (Ctrl+Shift+P / Cmd+Shift+P) and run TypeScript: Restart TS Server.

Lessons Learned

  • Skip as any and // @ts-ignore. They silence the error but guarantee a runtime bug later.
  • Form inputs, URL params, and environment variables are always strings. Convert them before putting them anywhere typed.
  • When an interface and the actual data disagree, fix the interface โ€” not the data.
  • ts(2322) is TypeScript doing its job. It's catching a bug that would only explode at runtime, probably in production.

Related Error Notes