What Are the Most Common TypeScript Compilation Errors and How to Fix Them?
What are the most common TypeScript compilation errors and how to fix them?
TL;DR
- Bottom line: 90% of TypeScript compilation errors come from 10 error codes: TS2322 (type mismatch), TS2345 (argument mismatch), TS2304 (cannot find name), TS2339 (property not found), TS2741 (missing property), TS7006 (implicit any), TS2307 (module not found), TS1005 (syntax), TS2532 (possibly undefined), and TS18048 (possibly undefined variable). Fix them by reading the error structurally: the last line tells you the root cause.
- Key tool/command:
tsc --noEmit --pretty— type-checks without generating output;--prettyformats errors with context lines and color. - Watch out for: Silencing errors with
as anyor@ts-ignore— this defeats TypeScript's purpose and creates hidden runtime bugs. Fix the type, don't suppress the error. - Works with: TypeScript 4.0–7.0 Beta. Error codes are stable across versions; strict mode is default in TS 7.0 (Apr 2026 Beta, Go-based compiler, ~10× faster than 6.0). [src9]
Constraints
- Never recommend
as anyor@ts-ignoreas a permanent fix — only as a temporary escape hatch with a tracking comment. - Always check
tsconfig.jsonstrictandtargetsettings before diagnosing — many errors only appear with specific compiler options enabled. - Error codes are stable, but their messages may differ slightly between TypeScript versions — always reference the error code number, not the message text.
- Do not assume
--skipLibCheckis safe — it hides real errors in.d.tsfiles that surface at runtime. - TS2322 and TS2345 are structural errors — TypeScript uses structural typing, not nominal typing, so solutions must account for shape compatibility, not class identity.
Quick Reference
| # | Error Code | Message Pattern | Likelihood | Cause | Fix |
|---|---|---|---|---|---|
| 1 | TS2322 | Type 'X' is not assignable to type 'Y' | ~25% | Assigning incompatible type to variable, return type, or property | Match the type: change the value, widen the target type, or use a type guard [src1, src5] |
| 2 | TS2345 | Argument of type 'X' is not assignable to parameter of type 'Y' | ~15% | Passing wrong type to a function parameter | Convert the value, fix the argument, or update the function signature [src5] |
| 3 | TS2339 | Property 'X' does not exist on type 'Y' | ~12% | Accessing a property not declared on the type | Add property to interface, use type guard, or check spelling [src5, src6] |
| 4 | TS2304 | Cannot find name 'X' | ~10% | Undeclared variable, missing import, or missing type definition | Import the symbol, install @types/ package, or declare the global [src5] |
| 5 | TS7006 | Parameter 'X' implicitly has an 'any' type | ~8% | Function parameter lacks type annotation (with noImplicitAny) |
Add explicit type annotation to the parameter [src2, src4] |
| 6 | TS2532 | Object is possibly 'undefined' | ~7% | Accessing property on possibly-null/undefined value (with strictNullChecks) |
Add null check, use optional chaining ?., or non-null assertion ! (with caution) [src4, src6] |
| 7 | TS2741 | Property 'X' is missing in type 'Y' but required in type 'Z' | ~6% | Object literal missing a required property | Add the missing property or mark it optional with ? in the interface [src5] |
| 8 | TS2307 | Cannot find module 'X' or its corresponding type declarations | ~5% | Module path wrong, package not installed, or missing @types/ |
Install the package, install @types/X, or create a declaration file [src5] |
| 9 | TS1005 | 'X' expected | ~4% | Syntax error — missing bracket, semicolon, or keyword | Check for missing }, ), ;, or mismatched template literals [src3, src6] |
| 10 | TS18048 | 'X' is possibly 'undefined' | ~3% | Variable-level undefined check (stricter variant of TS2532) | Narrow the type with an if check or optional chaining [src4] |
Decision Tree
START — TypeScript compilation error
├── Is it a TS2XXX error (type/semantic)?
│ ├── TS2322 / TS2345 — Type mismatch
│ │ ├── Primitive mismatch (string vs number)? → Change the value or the type annotation
│ │ ├── Object shape mismatch? → Add missing properties or use Partial<T>
│ │ ├── Union type issue? → Use a type guard to narrow the type
│ │ └── Function return type? → Fix the return statement or the declared return type
│ ├── TS2339 — Property does not exist
│ │ ├── Typo in property name? → Fix the spelling
│ │ ├── Property exists at runtime but not in type? → Extend the interface or use type assertion
│ │ └── Working with union type? → Narrow with type guard before accessing
│ ├── TS2304 — Cannot find name
│ │ ├── Missing import? → Add import statement
│ │ ├── Global (e.g., window, process)? → Install @types/node or add to global.d.ts
│ │ └── Third-party library? → Install @types/{lib} package
│ ├── TS2532 / TS18048 — Possibly undefined
│ │ ├── Optional property? → Use optional chaining: obj?.prop
│ │ ├── Array .find() result? → Add undefined check before use
│ │ └── Known to exist at runtime? → Use non-null assertion ! (last resort)
│ ├── TS2741 — Missing property
│ │ └── Object literal? → Add the property or make it optional in the interface
│ └── TS2307 — Cannot find module
│ ├── npm package? → npm install {package} && npm install -D @types/{package}
│ ├── Local file? → Check relative path and file extension
│ └── CSS/JSON/image? → Add declaration file (e.g., declarations.d.ts)
├── Is it a TS7XXX error (strict mode)?
│ ├── TS7006 — Implicit any
│ │ └── Add type annotation: (param: Type) => ...
│ └── TS7053 — Element implicitly has 'any' type (index access)
│ └── Add index signature: { [key: string]: ValueType }
├── Is it a TS1XXX error (syntax)?
│ └── TS1005 — Expected token
│ └── Check brackets, parentheses, and template literal backticks
└── DEFAULT → Read the last line of the error chain — it identifies the root cause [src1]
Step-by-Step Guide
1. Read the error message structurally
TypeScript error messages are chains. Read from the last line up — the final line identifies the root incompatibility. [src1]
Type '{ name: string; age: string; }' is not assignable to type 'User'.
Types of property 'age' are incompatible.
Type 'string' is not assignable to type 'number'.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the actual cause — age should be a number.
Verify: tsc --noEmit --pretty 2>&1 | head -20 → shows formatted error with context
2. Check your tsconfig.json strict settings
Many errors only appear when strict mode is enabled. Understand what is active. [src2, src4]
{
"compilerOptions": {
"strict": true, // Enables ALL of the below:
"noImplicitAny": true, // TS7006: no untyped params
"strictNullChecks": true, // TS2532: null/undefined checks
"strictFunctionTypes": true,
"strictBindCallApply": true,
"strictPropertyInitialization": true,
"noImplicitThis": true,
"alwaysStrict": true
}
}
Verify: tsc --showConfig | grep strict → shows effective strict settings
3. Fix TS2322 / TS2345 — type mismatches
The most common errors. Structural typing means shapes must match. [src1, src5]
// TS2322: Type 'string' is not assignable to type 'number'
let age: number = "25"; // Error
let age: number = Number("25"); // Fixed — explicit conversion
// TS2345: Argument of type 'string' is not assignable to parameter of type 'number'
function double(n: number): number { return n * 2; }
double("5"); // Error
double(Number("5")); // Fixed
// Object shape mismatch
interface User { name: string; age: number; }
const user: User = { name: "Alice" }; // TS2741: Property 'age' is missing
const user: User = { name: "Alice", age: 30 }; // Fixed
4. Fix TS2339 — property does not exist
Narrow the type before accessing properties on union types. [src5, src6]
// Error: Property 'email' does not exist on type 'User | Admin'
function getEmail(person: User | Admin): string {
return person.email; // Error if only Admin has email
}
// Fixed with type guard
function getEmail(person: User | Admin): string {
if ("email" in person) {
return person.email; // TypeScript narrows to Admin
}
return "no-email";
}
5. Fix TS2532 / TS18048 — possibly undefined
Use narrowing, optional chaining, or the nullish coalescing operator. [src4, src6]
// Error: Object is possibly 'undefined'
const users: User[] = [];
const first = users.find(u => u.active);
console.log(first.name); // TS2532
// Fixed: null check
const first = users.find(u => u.active);
if (first) {
console.log(first.name); // TypeScript knows first is defined here
}
// Fixed: optional chaining + fallback
console.log(first?.name ?? "unknown");
6. Fix TS2307 — cannot find module
Module resolution depends on moduleResolution in tsconfig.json. [src5]
# Missing npm package
npm install lodash
npm install -D @types/lodash
// declarations.d.ts — place in project root or src/
declare module "untyped-package"; // Minimal: types as any
declare module "*.css" {
const content: Record<string, string>;
export default content;
}
declare module "*.svg" {
const src: string;
export default src;
}
Verify: tsc --traceResolution 2>&1 | grep "untyped-package" → shows resolution path
Code Examples
TypeScript: Type guard patterns for eliminating common errors
// Input: Values with union types that trigger TS2339/TS2532
// Output: Type-safe access patterns that eliminate compilation errors
// 1. Discriminated union — eliminates TS2339
interface Circle { kind: "circle"; radius: number; }
interface Square { kind: "square"; side: number; }
type Shape = Circle | Square;
function area(shape: Shape): number {
switch (shape.kind) {
case "circle": return Math.PI * shape.radius ** 2; // TS knows: Circle
case "square": return shape.side ** 2; // TS knows: Square
}
}
// 2. User-defined type guard — eliminates TS2339 on unknown types
function isUser(obj: unknown): obj is User {
return typeof obj === "object" && obj !== null
&& "name" in obj && "age" in obj;
}
// 3. Assertion function — eliminates TS2532
function assertDefined<T>(val: T | undefined, msg: string): asserts val is T {
if (val === undefined) throw new Error(msg);
}
const item = items.find(i => i.id === targetId);
assertDefined(item, `Item ${targetId} not found`);
console.log(item.name); // No TS2532 — TypeScript knows item is defined
TypeScript: tsconfig.json strict migration strategy
// Input: Existing JS/TS project with no strict mode
// Output: Incremental strict mode adoption without breaking the build
// tsconfig.json — Phase 1: Enable strict gradually
// {
// "compilerOptions": {
// "strict": false,
// "noImplicitAny": true, // Phase 1
// "strictNullChecks": false, // Phase 2
// "strictFunctionTypes": false // Phase 3
// }
// }
// Phase 1: Fix all TS7006 (implicit any) errors
// Before:
function process(data) { return data.value; }
// After:
function process(data: { value: string }): string { return data.value; }
// Phase 2: Enable strictNullChecks, fix TS2532 errors
// Before:
const el = document.getElementById("app");
el.innerHTML = "Hello"; // TS2532: Object is possibly 'null'
// After:
const el = document.getElementById("app");
if (el) { el.innerHTML = "Hello"; }
// Phase 3: Enable strictFunctionTypes
// Catches unsafe contravariance in callback types
Anti-Patterns
Wrong: Silencing errors with as any
// BAD — as any hides the real type error; breaks at runtime [src6]
const data: any = fetchData();
const name: string = (data as any).user.name;
// If data.user is null, this throws at runtime — TypeScript can't help you
Correct: Use proper typing or unknown
// GOOD — type-safe with runtime check [src6]
interface ApiResponse { user: { name: string } | null; }
const data: ApiResponse = await fetchData();
const name: string = data.user?.name ?? "anonymous";
// TypeScript ensures null safety; no runtime surprise
Wrong: Using @ts-ignore instead of fixing the error
// BAD — suppresses ALL errors on the next line, including real bugs [src1]
// @ts-ignore
const result: number = "not a number" + someUndefinedVar;
// Both the type error AND the undefined variable are hidden
Correct: Use @ts-expect-error with explanation (or fix the type)
// GOOD — @ts-expect-error fails if the error disappears [src1]
// @ts-expect-error — third-party lib types are wrong, tracked in JIRA-1234
const result = thirdPartyLib.brokenMethod();
// BEST — fix the actual type
const result: number = parseInt(someValue, 10) || 0;
Wrong: Widening types to avoid errors
// BAD — making everything optional defeats type safety [src5]
interface User {
name?: string; // Was required, made optional to "fix" TS2741
email?: string; // Was required, made optional to "fix" TS2741
age?: number; // Was required, made optional to "fix" TS2741
}
// Now every access needs ?. and every consumer must handle undefined
Correct: Provide the required data or use Partial<T> explicitly
// GOOD — keep the interface strict; use Partial only where needed [src5]
interface User {
name: string;
email: string;
age: number;
}
// For creation forms where fields are filled incrementally:
type UserDraft = Partial<User>;
// For the final validated object:
function createUser(draft: UserDraft): User {
if (!draft.name || !draft.email || !draft.age) {
throw new Error("All fields required");
}
return { name: draft.name, email: draft.email, age: draft.age };
}
Wrong: Non-null assertion (!) everywhere
// BAD — ! tells TypeScript "trust me, it's not null" — but it might be [src4]
const element = document.getElementById("app")!;
element.innerHTML = "Hello"; // Crashes if #app doesn't exist
const user = users.find(u => u.id === id)!;
user.name = "Updated"; // Crashes if user not found
Correct: Explicit null checks
// GOOD — handle the null case explicitly [src4]
const element = document.getElementById("app");
if (!element) throw new Error("Required element #app not found in DOM");
element.innerHTML = "Hello"; // TypeScript knows element is HTMLElement
const user = users.find(u => u.id === id);
if (!user) {
console.error(`User ${id} not found`);
return;
}
user.name = "Updated"; // TypeScript knows user is defined
Common Pitfalls
- Reading error messages top-down instead of bottom-up: TypeScript error chains explain context from outer to inner. The last line is the root cause. Read it first, then trace upward. [src1]
- Forgetting
strictNullCheckschanges everything: WithstrictNullChecks: true, everyArray.find(),Map.get(), anddocument.querySelector()returnsT | undefined. You must narrow before use. [src2, src4] - Missing
@types/packages for third-party JS libraries: Libraries written in JavaScript need@types/{name}for TypeScript support. Without them, you get TS2307. Check DefinitelyTyped or usedeclare module. [src5] skipLibCheck: truehiding real declaration errors: This flag skips type checking in.d.tsfiles. It speeds up compilation but can hide incompatible type definitions between packages that only surface at runtime. [src2]- Using
anyin generic constraints:function process<T extends any>(val: T)provides zero type safety. Useunknownfor untyped inputs or a specific interface for constraints. [src6] - Confusing
typevsinterface: Useinterfacefor object shapes that may be extended; usetypefor unions, intersections, and computed types. Both work for most cases in TS 5.x+. [src1]
Diagnostic Commands
# Type-check without emitting (fastest error check)
npx tsc --noEmit --pretty
# Show all errors with file and line numbers
npx tsc --noEmit 2>&1 | head -50
# Count total errors
npx tsc --noEmit 2>&1 | grep "error TS" | wc -l
# Find errors of a specific code
npx tsc --noEmit 2>&1 | grep "error TS2322"
# Show effective tsconfig (see what strict flags are actually on)
npx tsc --showConfig
# Trace module resolution (debug TS2307)
npx tsc --traceResolution 2>&1 | grep "Module name"
# Watch mode — re-check on every save
npx tsc --noEmit --watch --pretty
# List all files tsc will compile
npx tsc --listFiles
# Generate declaration files to check type exports
npx tsc --declaration --emitDeclarationOnly
Version History & Compatibility
| Version | Status | Key Error Behavior Changes |
|---|---|---|
| TypeScript 7.0 Beta (Apr 2026) | Beta — stable in ~2 months | Go-based compiler (~10× faster than 6.0); strict: true is now the default; noUncheckedSideEffectImports and stableTypeOrdering default to true; target: es5, downlevelIteration, moduleResolution: node/node10, amd/umd/systemjs/none modules, and baseUrl removed; esModuleInterop and allowSyntheticDefaultImports cannot be false; module defaults to esnext; types defaults to [] (no auto-inclusion of @types/*) [src9] |
| TypeScript 6.0 (Mar 2026) | Final JS-based release | Transition release — adds deprecations, new default-value changes, and migration flag preparing for 7.0 |
| TypeScript 5.9 (Aug 2025) | Supported | import defer syntax; module: node20 mode; better tsc --init defaults (moduleDetection: force, target: esnext); MDN-sourced DOM API summaries; cached mapper instantiations; ~11% faster file-existence checks [src8] |
| TypeScript 5.8 (Feb 2025) | Supported | Stricter return type checking in conditional branches; require() of ESM in nodenext [src7] |
| TypeScript 5.7 (Nov 2024) | Supported | Improved detection of uninitialized variables; better error messages [src7] |
| TypeScript 5.5 (Jun 2024) | Supported | Inferred type predicates; better narrowing in control flow |
| TypeScript 5.4 (Mar 2024) | Supported | Narrowing in closures; NoInfer<T> utility type |
| TypeScript 5.0 (Mar 2023) | Supported | const type parameters; decorator metadata; --moduleResolution bundler |
| TypeScript 4.9 (Nov 2022) | Maintenance | satisfies operator (alternative to as for validation without widening) |
| TypeScript 4.0 (Aug 2020) | EOL | Variadic tuple types; labeled tuple elements |
Decision Logic
If error code is TS2322 or TS2345 and the mismatch is between primitives (string vs number)
Convert explicitly with Number(), String(), or Boolean() at the assignment site — do not widen the target type. [src1, src5]
If error code is TS2339 ("Property X does not exist") on a union type
Add a discriminant field (kind: "circle") or a user-defined type guard (function isX(o): o is X) before property access — never use (obj as any).prop. [src5, src6]
If error code is TS2532 or TS18048 ("possibly undefined") and the value comes from Array.find(), Map.get(), or document.querySelector()
Narrow with an if check or use optional chaining ?. + nullish coalescing ??; reserve non-null assertion ! for cases where the value is guaranteed by an outer invariant. [src4, src6]
If error code is TS2307 ("Cannot find module") for a third-party JS library
Run npm install -D @types/{pkg}; if no DefinitelyTyped package exists, create declarations.d.ts with declare module "{pkg}"; and file an issue upstream. [src5]
If error code is TS7006 ("implicitly has an any type")
Add an explicit parameter type annotation; if the input is genuinely untyped (e.g., JSON parse output), type it as unknown and narrow before use — never any. [src2, src4]
If your tsconfig predates 2026 and you want to upgrade to TypeScript 7.0
Before bumping the version: remove target: es5, downlevelIteration, baseUrl, and any legacy module setting (amd/umd/systemjs/none); switch moduleResolution: node to bundler or nodenext; ensure esModuleInterop and allowSyntheticDefaultImports are not explicitly false; explicitly list every @types/* package in types (auto-inclusion is gone). [src9]
If errors only appear in VS Code but tsc passes on the CLI
Pin the workspace TS version: set "typescript.tsdk": "node_modules/typescript/lib" in .vscode/settings.json and run TypeScript: Restart TS Server. [src2]
When to Use / When Not to Use
| Use When | Don't Use When | Use Instead |
|---|---|---|
You see a TS error code in compiler output |
Error is a runtime exception (no TS prefix) |
Check browser console / Node.js stack trace |
| Migrating JS to TS and need to fix type errors | Error is from ESLint / Prettier (not tsc) |
Check .eslintrc or Prettier config |
Enabling strict mode on an existing project |
Error is in a .js file with no // @ts-check |
Enable allowJs + checkJs in tsconfig |
| Type-checking fails in CI/CD pipeline | Error is only in IDE (VS Code) but tsc passes |
Restart TS Server: TypeScript: Restart TS Server |
| Third-party library has no types | Error is from a build tool (Webpack, Vite) | Check bundler config, not tsconfig |
Important Caveats
- TypeScript 7.0 Beta (Apr 2026) has already changed default behavior: The Go-based compiler makes
strict: truethe default and removestarget: es5,downlevelIteration, legacy modules (amd/umd/systemjs/none),baseUrl, andmoduleResolution: node/node10.esModuleInteropandallowSyntheticDefaultImportscan no longer be set tofalse. Projects that relied on these settings will not compile under 7.0 without migration. [src9] - Error codes are stable but messages evolve: TS2322 has been TS2322 since TypeScript 1.x, but the explanatory text improves with each release. Always reference error codes in documentation, not message strings.
strictNullChecksis the highest-impact strict flag: Enabling it on a large codebase can produce hundreds of new errors. Adopt it incrementally using// @ts-expect-errorannotations with tracking issues. [src2, src4]- IDE and CLI may report different errors: VS Code uses its own TS server version. Ensure
typescript.tsdkin.vscode/settings.jsonpoints to the project'snode_modules/typescript/libto match CI behavior. - Structural typing means extra properties are allowed in some contexts: TypeScript only reports excess property checks on object literals assigned directly. Assigning via a variable bypasses this check — a common source of confusion with TS2322.