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
stringor anumber? - 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 anyand// @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.

