This TypeScript error occurs when you try to assign a number value to a variable, parameter, or property that expects a string type. Fix it by using type conversion, union types, or correcting the type declaration.
This error is TypeScript's type checker preventing you from assigning a value of type 'number' to something that expects type 'string'. TypeScript uses structural typing to ensure type safety at compile time, and number and string are incompatible primitive types. The error typically appears as "TS2322: Type 'number' is not assignable to type 'string'" and indicates a mismatch between the declared type and the actual value being assigned. This can happen in variable assignments, function arguments, object properties, array elements, or return values. TypeScript's strict type system catches these mismatches before runtime, preventing potential bugs where string operations might be performed on numeric values or vice versa.
Check the TypeScript error message to find the specific line and variable causing the issue:
// Example error location
const userId: string = 123; // TS2322 error here
// ^^^^^^ Type 'number' is not assignable to type 'string'Look at what type was expected and what type was provided.
Use one of TypeScript's type conversion methods to convert the number to a string:
// Using .toString() method
const userId: string = 123; // ❌ Error
const userId: string = (123).toString(); // ✅ Works
// Using String() constructor
const count: string = String(42);
// Using template literals
const id: string = `${123}`;
// For function parameters
function logMessage(msg: string) {
console.log(msg);
}
logMessage(42); // ❌ Error
logMessage(42.toString()); // ✅ Works
logMessage(String(42)); // ✅ WorksIf your variable legitimately needs to handle both numbers and strings, use a union type:
// Allow both number and string
let userId: string | number = 123; // ✅ Works
userId = "abc-123"; // ✅ Also works
// For function parameters
function displayValue(value: string | number) {
console.log(value);
}
displayValue(123); // ✅ Works
displayValue("hello"); // ✅ Works
// For object properties
interface User {
id: string | number;
name: string;
}
const user: User = {
id: 123, // ✅ Works with union type
name: "Alice"
};If the variable should actually be a number, correct the type annotation:
// Before - incorrect type
const count: string = 42; // ❌ Error
// After - correct type
const count: number = 42; // ✅ Works
// For function return types
function getAge(): string { // ❌ Wrong return type
return 25;
}
function getAge(): number { // ✅ Correct return type
return 25;
}
// For interface properties
interface Product {
name: string;
price: string; // ❌ Should be number
}
interface Product {
name: string;
price: number; // ✅ Correct
}If you have a union type and need to handle both cases, use type guards:
function processId(id: string | number) {
// Type narrowing with typeof
if (typeof id === 'string') {
console.log(id.toUpperCase()); // string methods
} else {
console.log(id.toFixed(2)); // number methods
}
}
// Ensuring string type before assignment
let userId: string;
const rawId: string | number = getValue();
if (typeof rawId === 'string') {
userId = rawId; // ✅ TypeScript knows it's a string
} else {
userId = rawId.toString(); // Convert number to string
}Type assertions can override TypeScript's type checking, but use them cautiously:
// Type assertion (use sparingly)
const value = getUserInput();
const id: string = value as string; // Tells TypeScript to trust you
// Alternative assertion syntax
const id2: string = <string>value;
// Better approach: validate before asserting
function isString(val: unknown): val is string {
return typeof val === 'string';
}
const input: unknown = getInput();
if (isString(input)) {
const id: string = input; // ✅ Safe, type-checked
}Warning: Type assertions bypass type safety. Only use them when you have runtime validation or are certain of the type.
Type Compatibility in TypeScript
TypeScript uses structural typing (also called "duck typing"), where type compatibility is based on structure rather than explicit declarations. For primitive types like number and string, compatibility is strict—they are incompatible because they have different methods and behaviors.
Branded Types
For scenarios where you need distinct string types (like "UserId" vs "ProductId"), use branded types to prevent accidental mixing:
type UserId = string & { readonly brand: unique symbol };
type ProductId = string & { readonly brand: unique symbol };
const userId = "user-123" as UserId;
const productId = "prod-456" as ProductId;
function getUser(id: UserId) { /* ... */ }
getUser(userId); // ✅ Works
getUser(productId); // ❌ Error - different branded typesTemplate Literal Types
TypeScript 4.1+ supports template literal types for more precise string typing:
type NumericString = `${number}`;
const valid: NumericString = "123"; // ✅ Works
const invalid: NumericString = "abc"; // ❌ ErrorStrict Mode Impact
With strict: true in tsconfig.json, TypeScript is more aggressive about catching type mismatches. Consider your strictness settings if you're migrating JavaScript code:
{
"compilerOptions": {
"strict": true, // Enables all strict type checking
"strictNullChecks": true,
"strictFunctionTypes": true
}
}Working with External Libraries
If you encounter this error with third-party libraries, check:
1. Are you using the correct type definitions (@types packages)?
2. Is the library's TypeScript version compatible with yours?
3. Do you need to augment module declarations?
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript