TypeScript’s type checker is throwing a fit because it can’t reconcile two distinct types, meaning a value you’re trying to use isn’t shaped in a way that matches what the code expects.
This usually happens when you’re assigning a value from one variable or expression to another, and the compiler can’t guarantee that the source type is compatible with the target type. It’s a safety mechanism to prevent runtime errors by catching potential mismatches at compile time.
Here are the most common reasons you’re seeing TS2322:
-
Incorrectly typed function arguments or return values: You’re passing an argument to a function that doesn’t match the parameter’s type, or a function is returning a value that doesn’t match its declared return type.
- Diagnosis: Hover over the function call or the function definition in your IDE. TypeScript will highlight the mismatch.
- Fix: Ensure the type of the argument passed to the function precisely matches the type of the parameter. If it’s a return type issue, adjust the function’s implementation to return a value of the declared type, or update the function’s return type annotation to accurately reflect what it returns.
- Why it works: TypeScript enforces strict type compatibility. If
function foo(bar: string)is called withfoo(123), the compiler seesnumber(123) is not assignable tostring(bar).
-
Assigning a more general type to a more specific type: You’re trying to assign a value that could be
nullorundefinedto a variable that is explicitly typed not to be.- Diagnosis: Check the type annotation of the variable you’re assigning to. Look for strict null checks enabled in your
tsconfig.json("strictNullChecks": trueis the default in newer projects). - Fix: Either adjust the target variable’s type to allow
nullorundefined(e.g.,let myVar: string | null;), or ensure the value being assigned is guaranteed not to benull/undefined. A common pattern is using a non-null assertion operator (!) if you’re absolutely certain, though this bypasses type safety:myVar = potentiallyNullValue!;. - Why it works: If
strictNullChecksis on,stringis not assignable tostring | null | undefined. You must explicitly allownullorundefinedin the target type.
- Diagnosis: Check the type annotation of the variable you’re assigning to. Look for strict null checks enabled in your
-
Mismatched object property types: You’re assigning an object to a variable or passing it to a function, and one or more of its properties have types that don’t match the expected interface or type definition.
- Diagnosis: Compare the structure and types of the object you’re creating/using against the interface or type definition it’s supposed to conform to.
- Fix: Ensure every property on the source object has a type that is assignable to the corresponding property type in the target interface/type. If the source object is missing a required property, add it. If a property’s type is too narrow, widen it.
- Why it works: If
interface User { id: number; name: string; }and you haveconst user = { id: 1, name: 'Alice', age: 30 };, TypeScript will flagagebecause theUserinterface doesn’t have anageproperty. If you try to assign{ id: '123' }to a variable typed asUser, it will fail becausestring('123') is not assignable tonumber(id).
-
Using generic types incorrectly: When working with generic functions or classes, the type arguments you provide might not be compatible with the constraints defined on the generic type parameters.
- Diagnosis: Examine the generic type definition and the type arguments you’re supplying. Look for specific constraints (e.g.,
T extends { length: number }). - Fix: Ensure the types you’re using as type arguments satisfy all constraints. For example, if a generic function expects
T extends string, you cannot passnumberasT. - Why it works: Generics have rules. If
function processArray<T extends { id: number }>(items: T[])is called with[{ name: 'a' }], the compiler sees that{ name: 'a' }does not have anidproperty of typenumber, violating the constraint.
- Diagnosis: Examine the generic type definition and the type arguments you’re supplying. Look for specific constraints (e.g.,
-
Assigning an array of a specific type to an array of a more general type (or vice-versa unexpectedly): TypeScript’s array covariance/contravariance rules can be tricky. You can’t always assign
Derived[]toBase[]directly ifDerivedis a subtype ofBasewhen you expect it to work.- Diagnosis: Check the types of the arrays involved. For example,
string[]vs.any[]orstring[]vs.(string | number)[]. - Fix: Use
asor type assertions if you’re certain, but preferably, adjust the types to be compatible. Often, creating a new array or usingArray.prototype.mapto transform elements can resolve this. For example,const strings: string[] = ['a', 'b']; const general: (string | number)[] = [...strings];would work. - Why it works:
string[]is not assignable tonumber[]because you can’t safely treat a string as a number. Conversely, whilenumber[]can be assigned to(string | number)[], the other way around isn’t safe if the target array might contain numbers. The compiler will often warn if you try to assign(string | number)[]tostring[].
- Diagnosis: Check the types of the arrays involved. For example,
-
Third-party library types not matching your usage: If you’re using a library, its declared types might not align with how you’re trying to use its components, especially with older or poorly typed libraries.
- Diagnosis: Look at the type definitions (
.d.tsfiles) for the library or hover over the library’s imported types in your code. Compare them to how you are instantiating or calling them. - Fix: Update the library to the latest version if possible. If the issue persists, you might need to use type assertions (
as any) as a last resort, or, more ideally, create a declaration file (.d.ts) to augment or override the library’s types to match your specific usage. - Why it works: The compiler relies on the provided type definitions. If those definitions are inaccurate for your use case, mismatches occur.
- Diagnosis: Look at the type definitions (
The next error you’ll likely encounter is a runtime TypeError if you manage to bypass TypeScript’s checks, or another type mismatch error as you continue to fix the current one.