TypeScriptCommon Errors & Fixes

Common TypeScript Errors & Fixes

TypeScript's compiler catches bugs before they reach production, but its error messages can be cryptic at first. This page walks through the most common errors you will encounter, explains why they happen, and shows you exactly how to fix them.

TS2345 — Argument of type X is not assignable to parameter of type Y

This error fires when you pass a value to a function whose type does not match the parameter's expected type. It's one of the most frequent TypeScript errors and usually means there's a genuine type mismatch.

TS
// Error
function greet(name: string) {
  console.log(`Hello, ${name}!`);
}

const user = { name: 'Alice', age: 30 };
greet(user);
// TS2345: Argument of type '{ name: string; age: number; }'
//         is not assignable to parameter of type 'string'.

Fix: Pass the correct type — in this case, extract the name property:

TS
// Fix 1: Pass the right value
greet(user.name);

// Fix 2: Change the function to accept the object
function greetUser(user: { name: string }) {
  console.log(`Hello, ${user.name}!`);
}
greetUser(user); // OK
Tip
When you see TS2345, compare the reported types carefully. TypeScript tells you exactly what it received vs. what it expected — reading both sides resolves the fix quickly.
TS2322 — Type X is not assignable to type Y

Similar to TS2345 but occurs during assignment rather than function calls. You are storing a value in a variable or property that has an incompatible declared type.

TS
// Error
let age: number = "thirty";
// TS2322: Type 'string' is not assignable to type 'number'.

// Also common with literal types
type Direction = 'north' | 'south' | 'east' | 'west';
let dir: Direction = 'up';
// TS2322: Type '"up"' is not assignable to type 'Direction'.

TS
// Fix: Use the correct type
let age: number = 30;

// Fix: Use a valid union member
let dir: Direction = 'north';

// Fix: Widen the type if you genuinely need flexibility
let flexAge: number | string = "thirty";
Note
TS2322 frequently appears when an API returns any and you later assign it to a typed variable. Use type guards or assertions carefully to bridge the gap.
TS2339 — Property does not exist on type X

You are accessing a property that TypeScript does not know about on a given type. This often happens after JSON parsing, with third-party libraries, or when working with union types where only some members have the property.

TS
// Error
interface Cat {
  name: string;
  meow(): void;
}

const pet: Cat = { name: 'Whiskers', meow() {} };
pet.bark();
// TS2339: Property 'bark' does not exist on type 'Cat'.

// Error with union type
function handle(input: string | number) {
  console.log(input.toUpperCase());
  // TS2339: Property 'toUpperCase' does not exist on type 'string | number'.
  // (number has no toUpperCase)
}

TS
// Fix 1: Add the property to the interface
interface Pet {
  name: string;
  meow?(): void;
  bark?(): void;
}

// Fix 2: Narrow the union before accessing the property
function handle(input: string | number) {
  if (typeof input === 'string') {
    console.log(input.toUpperCase()); // Safe — narrowed to string
  }
}

// Fix 3: Use a type assertion when you are certain (use sparingly)
const data = JSON.parse('{"foo": 1}') as { foo: number };
console.log(data.foo); // OK
TS2571 — Object is of type 'unknown'

The unknown type is TypeScript's safer alternative to any. You can assign anything to unknown, but you can't do anything with it until you narrow its type. This error appears when you try to use an unknown value without checking what it actually is.

TS
// Error
function processInput(input: unknown) {
  console.log(input.toUpperCase()); // TS2571
  input.forEach((item: unknown) => {}); // TS2571 again
}

// Common in catch blocks (TypeScript 4+ makes caught errors 'unknown')
try {
  riskyOperation();
} catch (err) {
  console.log(err.message); // TS2571
}

TS
// Fix: Narrow with typeof / instanceof before use
function processInput(input: unknown) {
  if (typeof input === 'string') {
    console.log(input.toUpperCase()); // narrowed to string
  }
  if (Array.isArray(input)) {
    input.forEach((item) => console.log(item)); // narrowed to array
  }
}

// Fix for catch blocks
try {
  riskyOperation();
} catch (err) {
  if (err instanceof Error) {
    console.log(err.message); // OK
  } else {
    console.log(String(err)); // fallback
  }
}

// Create a reusable type guard helper
function isErrorWithMessage(err: unknown): err is { message: string } {
  return (
    typeof err === 'object' &&
    err !== null &&
    'message' in err &&
    typeof (err as Record<string, unknown>).message === 'string'
  );
}

try {
  riskyOperation();
} catch (err) {
  if (isErrorWithMessage(err)) {
    console.log(err.message);
  }
}
Tip
Prefer unknown over any for function parameters that accept arbitrary input. It forces callers to narrow before use, making bugs impossible to hide.
TS18048 — X is possibly 'undefined'

With strictNullChecks enabled (part of strict mode), TypeScript tracks undefined as a distinct type. This error appears when you use a value that might be undefined without first confirming it exists.

TS
// Error
const users = ['Alice', 'Bob', 'Charlie'];
// With noUncheckedIndexedAccess, array access returns string | undefined
const first = users[0];
console.log(first.toUpperCase());
// TS18048: 'first' is possibly 'undefined'.

// Also common with optional properties
interface Config {
  timeout?: number;
}
function connect(config: Config) {
  const ms = config.timeout * 1000;
  // TS18048: 'config.timeout' is possibly 'undefined'.
}

TS
// Fix 1: Explicit undefined check
const first = users[0];
if (first !== undefined) {
  console.log(first.toUpperCase()); // safe
}

// Fix 2: Non-null assertion (use only when you are certain it exists)
console.log(first!.toUpperCase());

// Fix 3: Optional chaining
console.log(first?.toUpperCase()); // returns undefined if first is undefined

// Fix 4: Nullish coalescing with a default
function connect(config: Config) {
  const ms = (config.timeout ?? 5000) * 1000;
}

// Fix 5: Provide a fallback with logical OR
const name = users[0] || 'Unknown';
Warning
Avoid the non-null assertion operator ! liberally — it silences the error but does not fix the underlying issue. Use it only when you have a guarantee TypeScript cannot infer.
Property does not exist on type 'never'

The never type represents a value that can never occur — it's the bottom type, meaning a type that has been narrowed to nothing. If TypeScript infers never for a variable, it means you have exhausted all possible types through narrowing, or there is a contradiction in your type logic.

TS
// Example: narrowing leaves nothing
function process(input: string | number) {
  if (typeof input === 'string') {
    // input is string here
  } else if (typeof input === 'number') {
    // input is number here
  } else {
    // input is 'never' here — no type left
    console.log(input);
    // Property 'foo' does not exist on type 'never'
  }
}

// Contradictory intersection
type Impossible = string & number; // never
// The type can never be satisfied

TS
// Understanding: 'never' in exhaustive checks is intentional
type Shape = 'circle' | 'square';

function area(shape: Shape): number {
  if (shape === 'circle') return Math.PI;
  if (shape === 'square') return 1;

  // TypeScript knows we've handled all cases
  // This assertion would error if Shape gains a new member — great for safety!
  const _exhaustive: never = shape;
  throw new Error(`Unhandled shape: ${_exhaustive}`);
}

// If you are hitting 'never' unexpectedly, your narrowing is too aggressive
// Widen the type or remove the impossible branch
Note
Seeing never in an unexpected place is TypeScript telling you that a code path is unreachable. This is often a sign of a logic error or an overly narrow type.
"Each member of the union type has signatures, but none are compatible"

This error occurs when you try to call a value typed as a union of functions, but the function signatures differ enough that TypeScript cannot find a safe common calling convention.

TS
// Error
type StringFn = (x: string) => void;
type NumberFn = (x: number) => void;
type EitherFn = StringFn | NumberFn;

declare const fn: EitherFn;
fn('hello');
// Error: Each member of the union type has signatures,
// but none of those signatures are compatible with each other.

// Why: if fn is StringFn, it CANNOT accept a number.
// if fn is NumberFn, it CANNOT accept a string.
// TypeScript cannot safely call it with either argument type.

TS
// Fix 1: Use a union-parameter function
type FlexFn = (x: string | number) => void;
declare const flexFn: FlexFn;
flexFn('hello'); // OK
flexFn(42);      // OK

// Fix 2: Use intersection for overloaded callables
type BothFn = ((x: string) => void) & ((x: number) => void);
declare const bothFn: BothFn;
bothFn('hello'); // OK
bothFn(42);      // OK

// Fix 3: Narrow to one branch before calling
if (typeof myArg === 'string') {
  (fn as StringFn)(myArg);
} else {
  (fn as NumberFn)(myArg);
}
"Type 'string' cannot be used as an index type"

This error appears when you use a string typed value to index an object that has specific known keys rather than a general string index signature.

TS
// Error
const colors = { red: '#f00', green: '#0f0', blue: '#00f' };

function getColor(name: string) {
  return colors[name];
  // Error: Element implicitly has an 'any' type because
  // expression of type 'string' can't be used to index
  // type '{ red: string; green: string; blue: string; }'.
}

TS
// Fix 1: Use keyof to restrict the parameter
function getColor(name: keyof typeof colors) {
  return colors[name]; // name is 'red' | 'green' | 'blue'
}

// Fix 2: Type guard with 'in'
function getColorSafe(name: string): string | undefined {
  if (name in colors) {
    return colors[name as keyof typeof colors];
  }
  return undefined;
}

// Fix 3: Add an index signature to the type
interface ColorMap {
  [key: string]: string;
}
const dynamicColors: ColorMap = { red: '#f00' };
function getDynColor(name: string) {
  return dynamicColors[name]; // OK
}

// Fix 4: Use Record<string, string>
const palette: Record<string, string> = { red: '#f00' };
"Cannot redeclare block-scoped variable"

This error appears when you declare a variable with the same name twice in the same scope using let or const. It also appears when TypeScript believes a global variable name conflicts with a local declaration — common in script files that have no import or export statements.

TS
// Error: obvious re-declaration
const username = 'Alice';
const username = 'Bob'; // Cannot redeclare block-scoped variable 'username'.

// Subtle error: file treated as a global script
// TypeScript treats the file as global, so 'name' conflicts with window.name:
const name = 'Alice'; // Cannot redeclare block-scoped variable 'name'.

TS
// Fix 1: Rename the variable
const username = 'Alice';
const updatedUsername = 'Bob';

// Fix 2: Make the file a module by adding an export or import
// This creates module scope and avoids global conflicts
export {};  // Turns the file into a module
const name = 'Alice'; // OK — module-scoped now

// Fix 3: Use a name that does not shadow globals
const userName = 'Alice'; // avoids conflict with 'name' global
Tip
If you see this error on a declaration that looks unique, check whether your file has any import or export statements. Without them, TypeScript treats the file as a global script.
"Module has no exported member"

This error occurs when you try to import a named export that does not exist in the target module. Common causes: typos, the export was removed or renamed, it is a default export, or the type definitions are outdated.

TS
// Error
import { getUserById } from './userService';
// Module has no exported member 'getUserById'.

// The module actually exports:
// export function fetchUser(id: number) { ... }
// export default class UserService { ... }

TS
// Fix 1: Use the correct export name
import { fetchUser } from './userService';

// Fix 2: Import the default export
import UserService from './userService';

// Fix 3: Import everything and inspect at runtime
import * as UserModule from './userService';

// Fix 4: Re-export with the expected name (if you control the module)
// In userService.ts:
export { fetchUser as getUserById } from './userService';
TS2304 — Cannot find name

TypeScript does not recognize a variable, type, or global name. Common causes are missing imports, typos, or missing lib configuration.

TS
// Error: Missing import
const [value, setValue] = useState(false);
// TS2304: Cannot find name 'useState'.

// Fix: Add the import
import { useState } from 'react';

// Error: Using browser globals without DOM lib
document.getElementById('app');
// TS2304: Cannot find name 'document'.

// Fix: Add "dom" to lib in tsconfig.json
// "lib": ["ES2020", "DOM", "DOM.Iterable"]

// Error: Using Node.js globals without @types/node
process.env.NODE_ENV;
// TS2304: Cannot find name 'process'.

// Fix: Install @types/node
// npm install --save-dev @types/node
TS2307 — Cannot find module

TypeScript cannot find a module you are importing — the module may not exist, the path may be wrong, or it may have no type definitions.

TS
// Error: no type definitions for lodash
import lodash from 'lodash';
// Could not find a declaration file for module 'lodash'.

// Error: wrong path
import { utils } from '../helpers/util';
// Cannot find module '../helpers/util'.

TS
// Fix 1: Install @types package
// npm install --save-dev @types/lodash
import lodash from 'lodash'; // now has types

// Fix 2: Declare the module yourself
// Create a file: src/types/lodash.d.ts
declare module 'lodash' {
  export function chunk<T>(array: T[], size: number): T[][];
}

// Fix 3: Wildcard declaration for untyped assets
declare module '*.png';
declare module '*.svg';
declare module 'some-untyped-lib';

// Fix 4: Check the path (case-sensitive on Linux/macOS)
import { utils } from '../helpers/utils'; // 'utils' not 'util'
Quick Reference: Error Codes

Error Code

Description

Common Cause

TS2345

Wrong argument type

Passing object where primitive expected

TS2322

Wrong assignment type

Mismatched variable type or literal union

TS2339

Property does not exist

Typo, wrong type, or union not narrowed

TS2571

Object is unknown

catch(err) blocks, JSON.parse result

TS18048

Possibly undefined

Optional property or array access without check

TS2304

Cannot find name

Missing import, typo, or missing lib

TS2307

Cannot find module

Missing package, wrong path, or no @types

TS2540

Cannot assign to readonly

Attempting to mutate a readonly property

TS7006

Implicit any parameter

Function param has no type with noImplicitAny

TS2554

Wrong argument count

Too many or too few arguments passed

TS2532

Object possibly undefined

Similar to TS18048, object access

TS2741

Missing properties

Partial object assigned to required-field type

General Debugging Strategy
  1. Read the full error message — TypeScript always reports the expected type AND the actual type.

  2. Hover over the variable in your editor to see its inferred type. This is faster than reading the error alone.

  3. Use Go to Definition on the type to understand its shape fully.

  4. Add a temporary type assertion: const _check: ExpectedType = value; — the error on this line pinpoints the mismatch.

  5. Check if strict mode or a specific flag like noUncheckedIndexedAccess was recently enabled.

  6. Search the TypeScript error code in the TypeScript GitHub issues for community explanations.

  7. Use // @ts-expect-error (not @ts-ignore) to suppress errors you intend to fix — it warns if the error disappears.

Success
Understanding TypeScript errors is a skill that compounds over time. Each time you resolve an error by fixing the type rather than suppressing it, you are building a codebase that is genuinely safer and easier to refactor at scale.