TypeScript errors

Status: 🟩 COMPLETE Last updated: 2026-06-21 Plain-English tagline: The TypeScript error messages that look intimidating but mean small things. Decode the wall-of-text; find the fix.


What this is

A lookup reference for TypeScript errors that come up in npm run build, in tsc --noEmit, or as red squiggles in VS Code. TypeScript’s error messages are notoriously verbose; this entry decodes the common patterns.

For broader build issues that include TS errors as one cause, see Build errors 🟩.


How to read a TypeScript error

A typical error has three layers:

Type '{ name: string; }' is not assignable to type 'User'.
  Property 'email' is missing in type '{ name: string; }' but required in type 'User'.

Read bottom-up: the last line is usually the actual problem (“you forgot the email field”). The first line is “TypeScript’s high-level complaint.” Long errors with nested types can stretch across 20 lines — almost always the actionable bit is at the end.


”Property ‘X’ does not exist on type ‘Y’”

What it means: You accessed .X on a value typed as Y, but Y doesn’t declare X.

Common causes:

  1. Typouser.emial instead of user.email.
  2. Type doesn’t include the property you expect — the type says { name: string } but you assumed email was there too.
  3. API response shape changed — the runtime gives you { name, email } but the type says only { name }.
  4. Using a generic that’s too narrowArray<string> doesn’t have your custom method.

Fix:

  • For typos: fix the spelling.
  • For “the type is wrong”: update the type definition to include the property. Don’t lie about the runtime.
  • For union types: narrow first. if ("email" in user) { user.email }.
// BEFORE
function greet(user: { name: string }) {
  return `Hi ${user.email}`;       // ❌ Property 'email' does not exist
}
 
// AFTER
function greet(user: { name: string; email: string }) {
  return `Hi ${user.email}`;       // ✅
}

”Type ‘X’ is not assignable to type ‘Y’”

What it means: You tried to assign (or pass) a value of type X somewhere expecting Y. They’re incompatible.

Common patterns:

  • String vs number — passing "42" where a number is expected. Convert: Number("42") or parseInt("42", 10).
  • Optional vs requiredstring | undefined not assignable to string. Narrow with a guard: if (x) { ... x ... }.
  • Subset vs superset — you have { name }, the function wants { name, email }. Add the missing field.
  • Wider type going to narrowerunknown not assignable to string. Narrow with typeof or as.

Fix: match the types. Don’t reach for as Y (type assertion) as a first move — it silences the compiler but can crash at runtime.


”Argument of type ‘X’ is not assignable to parameter of type ‘Y’”

What it means: Same as the assignment error, but specifically when calling a function.

Common cause for objects: TypeScript’s excess-property checks. Passing { name, extraField } to a function expecting { name }:

  • Object literal: errors (TypeScript catches the typo).
  • Pre-existing variable: doesn’t error (TypeScript allows wider types in pre-bound vars).
function setUser(u: { name: string }) {}
 
setUser({ name: "George", typo: 1 });    // ❌ object literal excess property
 
const u = { name: "George", typo: 1 };
setUser(u);                              // ✅ no error (wider type via variable)

Fix: when the error fires on an object literal, you usually mis-typed a field. Read carefully.


”Object is possibly ‘null’” / “Object is possibly ‘undefined’”

What it means: TypeScript thinks the value could be null/undefined, and you’re using it without a guard.

Fix paths:

  • Optional chaining: obj?.X — returns undefined instead of throwing.
  • Nullish coalescing: obj ?? defaultValue.
  • Type guard: if (obj !== null) { ... } or if (obj) { ... }.
  • Non-null assertion (use sparingly): obj!.X says “trust me, it’s not null."
function process(user: User | null) {
  return user?.name ?? "anonymous";      // safe
  // vs
  return user!.name;                     // crashes if user is null
}

"This expression is not callable” / “X is not a function”

What it means: You tried to call something that’s not a function (or whose type doesn’t say it’s a function).

Common causes:

  • The variable was declared with a different type and you forgot.
  • You imported the default vs a named export incorrectly (import x from "y" vs import { x } from "y").
  • The function is conditionally undefined.

Fix: check the import / the type declaration.


”No overload matches this call”

What it means: You’re calling a function with overloaded signatures, and the arguments you passed don’t match any of them.

TypeScript’s listing of each overload is helpful — read them. The first overload that fails tells you what shape was expected.

Common in React with event handlers:

// ❌ "No overload matches this call" — event handler signature wrong
<input onChange={(value: string) => setName(value)} />
 
// ✅ — event handler gets ChangeEvent
<input onChange={(e) => setName(e.target.value)} />

“Generic type ‘X’ requires N type argument(s)”

What it means: You used a generic like Array<> or Map<> without filling in its parameters.

Fix:

const items: Array = [];           // ❌
const items: Array<string> = [];   // ✅
 
const cache: Map = new Map();                // ❌
const cache: Map<string, User> = new Map();  // ✅

“Type ‘X’ is missing the following properties from type ‘Y’: a, b, c”

What it means: You’re trying to use X as Y, but Y requires additional fields that X doesn’t have.

Fix: add the missing fields, or change the function/type to accept the narrower input.

If many of Y’s fields are optional in your use case, consider Partial<Y> or Pick<Y, "needed-fields">:

type User = { id: string; name: string; email: string; phone: string; };
 
function logUser(u: Pick<User, "id" | "name">) { /* ... */ }
 
// Now you can call with just id + name
logUser({ id: "1", name: "George" });    // ✅

“‘X’ has no exported member ‘Y’”

What it means: You’re importing something from a module that doesn’t export it.

Common causes:

  1. Typo in the named import.
  2. Library version mismatch — older API uses a different name.
  3. Default vs named confusionimport Foo from "x" vs import { Foo } from "x".
  4. The export was removed in a major version bump.

Fix: open the module’s index.d.ts (in node_modules/<package>/) or its docs. The actual exports list is there.


”Cannot find module ‘X’ or its corresponding type declarations”

What it means: The package is installed (or you’re using a custom path), but no type declarations exist.

Fix:

  • For npm packages: npm install -D @types/X.
  • Modern packages ship their own types — if @types/X doesn’t exist, the package probably types itself. Check package.json"types".
  • For internal modules with no types: create X.d.ts with declare module "X"; (a quick stub).

”Property ‘X’ has no initializer and is not definitely assigned in the constructor”

What it means: With strict: true, class properties must be initialized.

Fix:

// ❌
class User {
  name: string;
}
 
// ✅ Option 1: initialize
class User {
  name: string = "";
}
 
// ✅ Option 2: definite assignment assertion
class User {
  name!: string;  // "I promise this gets set elsewhere"
}
 
// ✅ Option 3: mark optional
class User {
  name?: string;
}

“Element implicitly has an ‘any’ type because index expression is not of type ‘number’”

What it means: You’re indexing into a type with a key TypeScript can’t verify.

Common pattern:

const colors = { red: "#f00", green: "#0f0", blue: "#00f" };
 
function getColor(name: string) {
  return colors[name];   // ❌ TS doesn't know `name` is a valid key
}

Fix:

  • Constrain the parameter: function getColor(name: keyof typeof colors).
  • Type the object with an index signature: const colors: Record<string, string> = { ... }.
  • Cast at access: colors[name as keyof typeof colors] (last resort).

”Type instantiation is excessively deep and possibly infinite”

What it means: A type definition is recursive in a way TypeScript can’t resolve.

Common in advanced generic code. Most app code doesn’t hit this.

Fix: simplify the type. Break recursion with explicit base cases. If the type comes from a library, file an issue or pin to a working version.


”Cannot redeclare block-scoped variable ‘X’”

What it means: You declared the same variable name twice in the same scope. Usually means the file is being treated as a script (global scope), not a module.

Fix: ensure the file has at least one import or export. If it has neither and is meant to be a module, add export {}; at the end.


”’X’ refers to a value, but is being used as a type here”

What it means: You used a runtime value where TypeScript expected a type.

Fix: use typeof X to convert a value to its type:

const config = { theme: "dark", count: 5 };
 
function setConfig(c: config) {}              // ❌ 'config' is a value
function setConfig(c: typeof config) {}       // ✅

“Subsequent property declarations must have the same type” (interface merging)

What it means: Two interface declarations of the same name don’t agree on a property’s type.

Common cause: library augmentation. You added a type for Window.foo somewhere, and the same property was already declared elsewhere with a different type.

Fix: unify the types, or scope the augmentation to a narrow declare global { ... } block.


See also


Sources