TypeScript Errors Explained: The Ten You Hit Most
Read the error as a contract mismatch: the type you provided and the type the code requires. The fix is deciding which side is right.
TS2322: Type Is Not Assignable
let n: number = "5"; // Type 'string' is not assignable to type 'number'The three real-world shapes: (1) literal values — convert explicitly (Number(s)) or fix the annotation; (2) object shapes — a missing or extra property against an interface (the message lists the exact property); (3) null/undefined under strictNullChecks — the type is T | undefined and you assigned just T. Fix with a guard (if (x !== undefined)) or make the target type accept undefined. Resist as any: it silences this error and exports the bug to runtime.
TS2345: Argument of Type X Is Not Assignable to Parameter of Type Y
function greet(name: string) {}
greet(42); // Argument of type 'number' is not assignable...Same family as 2322, at a call site. When the argument "looks right", the mismatch is usually the object's shape — missing optional handling, extra property (excess property checks reject it on literals), or a narrower union than the signature wants. Compiler suggestion: "Did you mean to call with X?" often names the correct conversion.
TS2339: Property Does Not Exist on Type
const user = {};
user.name; // Property 'name' does not exist on type '{}'Common causes: (1) the object type was never declared — annotate it or let it be inferred from a value that has the property; (2) union without narrowing — if ('name' in x) or a discriminated union with a kind field; (3) API response typed too loosely — define the response interface and cast the parsed JSON once at the boundary. The "does not exist on type 'never'" variant means TypeScript proved the branch impossible — usually an earlier narrowing bug.
TS7006: Parameter Implicitly Has an 'any' Type
[1, 2, 3].map((x) => x * 2); // only errors in strict modeUnder noImplicitAny, callbacks need types when the compiler can't infer them. Fixes: annotate ((x: number) — usually the array infers it, so this fires when the array itself is any[]); or better, type the array so inference flows. In React event handlers, the fix is the event type: (e: React.ChangeEvent<HTMLInputElement>).
TS18048: 'x' Is Possibly 'Undefined'
const len = arr.length; // 'arr' is possibly 'undefined'strictNullChecks working as intended: the value may be absent and you used it anyway. Fixes: optional chaining (arr?.length ?? 0), early return guards, or redesign to not have optional data. Using ! (non-null assertion) is a promise to the compiler — only make it when you can prove it locally, or you have converted a compile error into a runtime one.
TS2554 / TS2551 / TS2741
| Code | Message | Fix |
|---|---|---|
| TS2554 | Expected N arguments, but got M | Match the signature; check optional params (?) |
| TS2551 | Property 'X' does not exist. Did you mean 'Y'? | Typo — TS computed the suggestion via edit distance |
| TS2741 | Property 'X' is missing in type Y but required in Z | Add the property, or mark it optional in the interface if legitimately absent |
TS2769: No Overload Matches This Call
Library overloads (React's useState, Express handlers) produce this when arguments don't match any overload signature. Read the LAST overload in the error — TypeScript lists all candidates and the final one is usually the most generic; the mismatch against it names the real problem (commonly: wrong event type, or a callback whose parameter lacks a type annotation).
The Strategic Decisions Behind the Errors
- Annotate boundaries, infer internals. Types at module edges (function signatures, API responses) buy safety; annotations on every local variable add noise and drift.
- unknown, not any, for unvalidated data.
anydisables checking silently;unknownforces a narrowing step — a runtime check the compiler can verify. - Discriminated unions over optional fields.
{ kind: 'ok', value: T } | { kind: 'err', message: string }makes impossible states unrepresentable and replaces TS2339 with clean narrowing. - Satisfies for literals.
const cfg = {...} satisfies Configchecks the value without widening its type — better than an annotation when you want both validation and precise inference.
When to Actually Use a Cast
Casts (as T) are correct at exactly one place: where you hold information the compiler cannot (parsed JSON from a validated schema, dom.querySelector results you just created). Everywhere else, a cast is an assertion that will eventually be a lie — and TypeScript errors suppressed today become "Cannot read properties of undefined" tomorrow. Full circle: the undefined guide.
Related Errors
JavaScript errors cheat sheet · Cannot read properties of undefined