Learn why TypeScript throws TS2322 when a value doesn't match the expected type, and how to fix type assignability errors with practical examples.
TS2322 is the most common TypeScript error you will encounter. It fires whenever you try to put a value into a slot that expects a different type. Think of it as TypeScript telling you: "You promised this would be an X, but you're giving me a Y."
This is the backbone of TypeScript's type system. Every time you assign a value to a variable, pass a prop to a React component, or return a value from a function, TypeScript checks that the types line up. When they don't, you get TS2322.
// The general shape of the error:
// Type 'string' is not assignable to type 'number'.
// ~~~~~~ ~~~~~~
// what you gave what was expectedThis is the simplest case. You declared a variable with one type but tried to assign a value of another.
// ❌ Broken
let userAge: number = "twenty-five"
// ~~~~~~~ Error: Type 'string' is not assignable to type 'number'.// ✅ Fixed — use the correct type
let userAge: number = 25When you assign an object literal directly, TypeScript performs excess property checking. Any property not defined in the target type triggers TS2322.
// ❌ Broken
interface UserProfile {
name: string
email: string
}
const profile: UserProfile = {
name: "Ada Lovelace",
email: "ada@example.com",
role: "admin", // Error: Object literal may only specify known properties.
}// ✅ Fixed — remove the extra property or extend the interface
interface UserProfile {
name: string
email: string
role?: string // add it as optional if needed
}
const profile: UserProfile = {
name: "Ada Lovelace",
email: "ada@example.com",
role: "admin",
}If your function's return type annotation doesn't match what you actually return, TypeScript catches it.
// ❌ Broken
function getDiscountPercentage(tier: string): number {
if (tier === "premium") return 20
return "none"
// ~~~~~~ Error: Type 'string' is not assignable to type 'number'.
}// ✅ Fixed — return a number for all paths
function getDiscountPercentage(tier: string): number {
if (tier === "premium") return 20
return 0
}In React projects, TS2322 often appears when a component receives props that don't match its type definition.
// ❌ Broken
interface ButtonProps {
variant: "primary" | "secondary"
disabled: boolean
}
function AppButton(props: ButtonProps) { /* ... */ }
// In another component:
<AppButton variant="danger" disabled={false} />
// ~~~~~~~ Error: Type '"danger"' is not assignable to type '"primary" | "secondary"'.// ✅ Fixed — use a valid variant
<AppButton variant="primary" disabled={false} />Follow these steps when you see TS2322:
Read the full error message. TypeScript tells you the source type (what you provided) and the target type (what was expected). Start there.
Check the variable or parameter declaration. Go to where the type is defined. Is the annotation correct, or does it need to be widened?
Check the value you're assigning. Is it coming from an API response, user input, or another function? The value itself might be wrong.
Decide which side to fix:
Number(), String(), or Boolean() rather than type assertions.Avoid as casts as a first resort. Type assertions silence the error but don't fix the underlying problem. They can hide real bugs that surface at runtime.
// Prefer explicit conversion over assertion
const inputValue = document.getElementById("age") as HTMLInputElement
const userAge: number = Number(inputValue.value) // safe conversion
// Avoid this pattern
const userAge: number = inputValue.value as unknown as number // unsafe!TS2322 occurs when you try to assign a value to a variable, parameter, or property whose type doesn't accept that value. This is TypeScript's core type safety check preventing type mismatches.
For example, if you declare let count: number and then write count = "five", TypeScript raises TS2322 because string is not assignable to number. The same logic applies to function return types, object properties, and React component props.
You have three options depending on what's correct for your situation:
// Option 1: Change the annotation to accept strings
let quantity: string | number = "ten"
// Option 2: Convert the value to the expected type
let quantity: number = Number("10")
// Option 3: Fix the source of the value
let quantity: number = getQuantityFromCart() // ensure this returns numberThe right choice depends on your domain logic. If the value genuinely should be a number, convert it or fix the source. If both types are valid, widen the annotation with a union type.
Technically yes, but it is almost always a bad idea:
// @ts-ignore — suppresses the error on the next line
// @ts-expect-error — same, but errors if there's nothing to suppress (safer)
const userId: number = "abc" as anyBoth @ts-ignore and type assertions (as) bypass the type system entirely. This means TypeScript can no longer protect you from runtime type errors on that line. Reserve these escape hatches for genuinely exceptional cases like working around third-party library type bugs, and always leave a comment explaining why.
Put your understanding to the test with these related challenges.
Get the latest TypeScript tips, tutorials, and course updates delivered straight to your inbox.