Emmanuel Akinfulubi

Error catalogue

38 compiler messages you will actually hit, what each one means, and what to do about it. Every code on this page was produced by the compiler during the build of this site — none of them are remembered or copied from elsewhere.

That guarantee is worth spelling out, because error references go stale quietly. Each entry below carries a sample that the build compiles and checks against the code it claims. If the compiler changed a message or renumbered an error, this site would fail to build until the page was fixed.

Index

Ordered by how often you are likely to meet them, not numerically.

TS2322

Type X is not assignable to type Y — lesson 02

TS2345

Argument of type X is not assignable to parameter of type Y — lesson 06

TS2339

Property X does not exist on type Y — lesson 03

TS2551

Property X does not exist. Did you mean Y? — lesson 03

TS2741

Property X is missing in type A but required in type B — lesson 03

TS2353

Object literal may only specify known properties — lesson 03

TS7006

Parameter X implicitly has an any type — lesson 01

TS18047

X is possibly null — lesson 08

TS18048

X is possibly undefined — lesson 08

TS2532

Object is possibly undefined — lesson 07

TS18046

X is of type unknown — lesson 12

TS2366

Function lacks ending return statement — lesson 04

TS2540

Cannot assign to X because it is a read-only property — lesson 03

TS2739

Type A is missing the following properties from type B — lesson 03

TS2352

Conversion of type A to type B may be a mistake — lesson 13

TS2367

This comparison appears unintentional — lesson 04

TS2693

X only refers to a type, but is being used as a value — lesson 01

TS2749

X refers to a value, but is being used as a type — lesson 10

TS2552

Cannot find name X. Did you mean Y? — lesson 15

TS2304

Cannot find name X — lesson 15

TS2307

Cannot find module X or its corresponding type declarations — lesson 15

TS2454

Variable X is used before being assigned — lesson 08

TS2564

Property X has no initializer and is not definitely assigned — lesson 18

TS2341

Property X is private and only accessible within class Y — lesson 11

TS2420

Class X incorrectly implements interface Y — lesson 11

TS2416

Property X in type A is not assignable to the same property in base type B — lesson 11

TS4114

This member must have an override modifier — lesson 11

TS2511

Cannot create an instance of an abstract class — lesson 11

TS2493

Tuple type X of length N has no element at index M — lesson 07

TS4104

The type is readonly and cannot be assigned to the mutable type — lesson 07

TS2375

Type undefined is not assignable under exactOptionalPropertyTypes — lesson 08

TS2344

Type X does not satisfy the constraint Y — lesson 16

TS2578

Unused @ts-expect-error directive — lesson 18

TS7053

Element implicitly has an any type because expression of type string cannot index — lesson 10

TS2769

No overload matches this call — lesson 06

TS1016

A required parameter cannot follow an optional parameter — lesson 06

TS1064

The return type of an async function must be Promise<T> — lesson 06

TS2554

Expected N arguments, but got M — lesson 06

01

TS2322

Lesson 02

Type X is not assignable to type Y

The most common error in the language, and the most general. You put a value somewhere that does not accept it. Everything from a typo in a union member to a badly shaped object ends up here.

What to do. Read the two type names in the message and ask which one is wrong. Usually the value; occasionally the annotation. If the types look identical, the difference is a modifier — readonly, optional, or a literal versus a widened type.

Type X is not assignable to type Y

TS2322
type Level = 'debug' | 'info';

const level: Level = 'warn';

console.log(level);

What the compiler says

error TS2322: Type '"warn"' is not assignable to type 'Level'.
02

TS2345

Lesson 06

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

The same thing as TS2322, at a call site. The distinction is useful: the problem is what you passed, not what you declared.

What to do. Check the argument order first — two parameters of different types swapped produces exactly this. Then check whether the parameter is narrower than you remembered.

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

TS2345
function repeat(text: string, times: number): string {
  return text.repeat(times);
}

console.log(repeat(3, 'ab'));

What the compiler says

error TS2345: Argument of type 'number' is not assignable to parameter of type 'string'.
03

TS2339

Lesson 03

Property X does not exist on type Y

You reached for something that is not on the type. Either it genuinely is not there, or the value is a union and the property only exists on some members.

What to do. If it is a union, narrow first — lesson 05. If you are certain the property exists, the type is wrong rather than the code, which usually means a bad annotation or a library definition that needs augmenting.

Property X does not exist on type Y

TS2339
type Id = string | number;

function shout(id: Id): string {
  return id.toUpperCase();
}

console.log(shout('a-1'));

What the compiler says

error TS2339: Property 'toUpperCase' does not exist on type 'Id'.
04

TS2551

Lesson 03

Property X does not exist. Did you mean Y?

TS2339 with a suggestion. The compiler found a member with a similar name, which means this is almost always a typo.

What to do. Accept the suggestion. This error existing separately is a small kindness — it means the compiler is confident enough to guess.

Property X does not exist. Did you mean Y?

TS2551
interface User {
  firstName: string;
}

const user: User = { firstName: 'Ada' };

console.log(user.firstname);

What the compiler says

error TS2551: Property 'firstname' does not exist on type 'User'. Did you mean 'firstName'?
05

TS2741

Lesson 03

Property X is missing in type A but required in type B

An object is short of something. Note that this is about a missing property specifically, which makes it more actionable than a bare TS2322.

What to do. Add the property, or make it optional with ? if it genuinely need not be there. If the property should have been inherited from somewhere, check that the spread or the intersection is doing what you think.

Property X is missing in type A but required in type B

TS2741
interface Point {
  x: number;
  y: number;
}

const p: Point = { x: 1 };

console.log(p);

What the compiler says

error TS2741: Property 'y' is missing in type '{ x: number; }' but required in type 'Point'.
06

TS2353

Lesson 03

Object literal may only specify known properties

Excess property checking. A fresh object literal in an assignment position is not allowed extra properties, even though a variable holding the same object would be.

What to do. If the extra property is a typo, fix it. If it is deliberate, assign through a variable first, or widen the target type. The rule exists specifically to catch typos at call sites, so a surprise here is usually the rule working.

Object literal may only specify known properties

TS2353
interface Options {
  retries: number;
}

function run(options: Options): number {
  return options.retries;
}

console.log(run({ retries: 3, timeout: 500 }));

What the compiler says

error TS2353: Object literal may only specify known properties, and 'timeout' does not exist in type 'Options'.
07

TS7006

Lesson 01

Parameter X implicitly has an any type

noImplicitAny. A parameter has no annotation and nothing to infer from, so the compiler refuses to silently give up.

What to do. Annotate it. If it is a callback that used to be inline, the context that was typing it disappeared when you extracted it — lesson 02.

Parameter X implicitly has an any type

TS7006
const shout = (word) => word.toUpperCase();

console.log(shout('hello'));

What the compiler says

error TS7006: Parameter 'word' implicitly has an 'any' type.
08

TS18047

Lesson 08

X is possibly null

strictNullChecks doing its job. The value's type includes null and you used it without checking.

What to do. Check with === null, or use ?. and ??. Reach for ! only when you have information the compiler cannot, and say what it is in a comment.

X is possibly null

TS18047
function initial(name: string | null): string {
  return name.charAt(0);
}

console.log(initial('Ada'));

What the compiler says

error TS18047: 'name' is possibly 'null'.
09

TS18048

Lesson 08

X is possibly undefined

The same as TS18047 for undefined. Usually an optional property, an optional parameter, or the result of find.

What to do. Default it with ??, guard it, or restructure so the value cannot be absent. A boundary function that converts uncertain data into certain data — lesson 08 — removes whole clusters of these at once.

X is possibly undefined

TS18048
interface Profile {
  bio?: string;
}

function length(profile: Profile): number {
  return profile.bio.length;
}

console.log(length({}));

What the compiler says

error TS18048: 'profile.bio' is possibly 'undefined'.
10

TS2532

Lesson 07

Object is possibly undefined

Nearly always noUncheckedIndexedAccess on an index access. arr[i] is T | undefined, because the compiler cannot know the array is long enough.

What to do. Iterate instead of indexing, which avoids it entirely. Otherwise check, default with ??, or assert with ! when the bounds are provably safe.

Object is possibly undefined

TS2532
const values: number[] = [1, 2, 3];

for (let i = 0; i < values.length; i++) {
  console.log(values[i].toFixed(2));
}

What the compiler says

error TS2532: Object is possibly 'undefined'.
11

TS18046

Lesson 12

X is of type unknown

You tried to use an unknown without establishing what it is. Most often a caught error, or the result of JSON.parse assigned to unknown.

What to do. Narrow it — typeof, instanceof, or a type predicate from lesson 13. Do not reach for as; the whole reason the value is unknown is that nobody has checked it yet.

X is of type unknown

TS18046
try {
  throw new Error('failed');
} catch (error) {
  console.log(error.message);
}

What the compiler says

error TS18046: 'error' is of type 'unknown'.
12

TS2366

Lesson 04

Function lacks ending return statement

A code path reaches the end of the function without returning, and the return type does not include undefined. On a switch over a union this usually means you missed a member.

What to do. Add the missing case, or a default. Better: add an assertNever default as lesson 04 shows, so the error names the member you forgot rather than just pointing at the closing brace.

Function lacks ending return statement

TS2366
type Shape =
  | { kind: 'circle'; radius: number }
  | { kind: 'square'; side: number };

function area(shape: Shape): number {
  switch (shape.kind) {
    case 'circle':
      return Math.PI * shape.radius ** 2;
  }
}

console.log(area({ kind: 'circle', radius: 1 }));

What the compiler says

error TS2366: Function lacks ending return statement and return type does not include 'undefined'.
13

TS2540

Lesson 03

Cannot assign to X because it is a read-only property

A readonly property, or a property of a Readonly<T>, or an element of a readonly array.

What to do. Build a new object with the change rather than mutating — { ...original, field: next }. If you genuinely need mutation, the readonly is the thing that is wrong.

Cannot assign to X because it is a read-only property

TS2540
interface Config {
  readonly host: string;
}

const config: Config = { host: 'localhost' };

config.host = 'example.com';

console.log(config);

What the compiler says

error TS2540: Cannot assign to 'host' because it is a read-only property.
14

TS2739

Lesson 03

Type A is missing the following properties from type B

The plural of TS2741 — more than one property is absent, so the compiler lists them.

What to do. Same as TS2741. When the list is long, the usual cause is that you are constructing a type meant to be derived; check whether Pick or Partial from lesson 16 is what you wanted.

Type A is missing the following properties from type B

TS2739
interface User {
  id: number;
  name: string;
  email: string;
}

const user: User = { id: 1 };

console.log(user);

What the compiler says

error TS2739: Type '{ id: number; }' is missing the following properties from type 'User': name, email
15

TS2352

Lesson 13

Conversion of type A to type B may be a mistake

An as between two types with no overlap. The compiler allows assertions that narrow or widen, and refuses ones that are simply unrelated.

What to do. If you are sure, the message tells you the escape: route through unknown. That should feel uncomfortable, because you have just switched off the last check — a type predicate is usually the right answer instead.

Conversion of type A to type B may be a mistake

TS2352
const value = 'a string';

const n = value as number;

console.log(n);

What the compiler says

error TS2352: Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.
16

TS2367

Lesson 04

This comparison appears unintentional

You compared two types that can never be equal, so the comparison is always false. Frequently a stale literal after a union was renamed.

What to do. Check the spelling of the literal, and check whether the union still contains the member you are comparing against. This error catches a genuine class of dead code.

This comparison appears unintentional

TS2367
type Level = 'debug' | 'info';

function isLoud(level: Level): boolean {
  return level === 'error';
}

console.log(isLoud('info'));

What the compiler says

error TS2367: This comparison appears to be unintentional because the types 'Level' and '"error"' have no overlap.
17

TS2693

Lesson 01

X only refers to a type, but is being used as a value

You used an interface or type where a runtime value is required — instanceof, typeof, new, or an argument position. Types do not exist at run time.

What to do. For a runtime check, write a type predicate (lesson 13) or use a class, which is a real value. If you wanted the type of a value, note that typeof x in a type position is a different operator and does work.

X only refers to a type, but is being used as a value

TS2693
interface Person {
  name: string;
}

console.log(typeof Person);

What the compiler says

error TS2693: 'Person' only refers to a type, but is being used as a value here.
18

TS2749

Lesson 10

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

The mirror image. You used a variable or function name where a type was expected.

What to do. Add typeof: const x: typeof defaults = .... This is the single most common cause of confusion around the two meanings of typeof, and the fix is almost always just adding it.

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

TS2749
const defaults = { retries: 3 };

function configure(options: defaults): number {
  return options.retries;
}

console.log(configure(defaults));

What the compiler says

error TS2749: 'defaults' refers to a value, but is being used as a type here. Did you mean 'typeof defaults'?
19

TS2552

Lesson 15

Cannot find name X. Did you mean Y?

An unknown identifier that closely resembles a known one. Like TS2551 for properties, the suggestion means the compiler is confident, so this is nearly always a typo.

What to do. Take the suggestion.

Cannot find name X. Did you mean Y?

TS2552
function save(data: string): void {
  localStorge.setItem('key', data);
}

save('x');

What the compiler says

error TS2552: Cannot find name 'localStorge'. Did you mean 'localStorage'?
20

TS2304

Lesson 15

Cannot find name X

An identifier the compiler has never heard of, with no near match to suggest. Either a missing import, or a global your lib and @types settings do not include.

What to do. If it is a browser or Node global, check lib and your @types packages — document needs "dom", process needs @types/node. If it is yours, you forgot the import.

Cannot find name X

TS2304
function save(data: string): void {
  frobnicator.setItem('key', data);
}

save('x');

What the compiler says

error TS2304: Cannot find name 'frobnicator'.
21

TS2307

Lesson 15

Cannot find module X or its corresponding type declarations

The import path does not resolve, or it resolves to JavaScript with no types.

What to do. Three candidates, in order: the path is wrong; the package has no types, so install @types/name or write a declare module; or your moduleResolution wants a file extension you did not write.

Cannot find module X or its corresponding type declarations

TS2307
import { helper } from './does-not-exist.js';

console.log(helper);

What the compiler says

error TS2307: Cannot find module './does-not-exist.js' or its corresponding type declarations.
22

TS2454

Lesson 08

Variable X is used before being assigned

A code path reaches a read before any write. The compiler traced your branches and found one that misses the assignment.

What to do. Give it an initial value, or add the else the analysis is pointing at. A definite assignment assertion (let x!: T) silences it and should be rare enough to justify in a comment.

Variable X is used before being assigned

TS2454
function classify(n: number): string {
  let label: string;

  if (n < 0) label = 'negative';
  else if (n > 0) label = 'positive';

  return label;
}

console.log(classify(0));

What the compiler says

error TS2454: Variable 'label' is used before being assigned.
23

TS2564

Lesson 18

Property X has no initializer and is not definitely assigned

strictPropertyInitialization. A class field is declared and never assigned, in the declaration or the constructor.

What to do. Assign it in the constructor, give it a default, or make its type include undefined. If a framework assigns it outside your control, declare or ! is the honest escape.

Property X has no initializer and is not definitely assigned

TS2564
class Account {
  balance: number;

  constructor() {}
}

console.log(new Account().balance);

What the compiler says

error TS2564: Property 'balance' has no initializer and is not definitely assigned in the constructor.
24

TS2341

Lesson 11

Property X is private and only accessible within class Y

You reached a private or # member from outside. Note the difference: # is enforced at run time as well; private is only checked here.

What to do. Add a method or getter that exposes what the caller actually needs. Reaching in through a cast works for private and not for #, which is one reason to prefer #.

Property X is private and only accessible within class Y

TS2341
class Account {
  private balance = 0;
}

console.log(new Account().balance);

What the compiler says

error TS2341: Property 'balance' is private and only accessible within class 'Account'.
25

TS2420

Lesson 11

Class X incorrectly implements interface Y

A class promised to satisfy an interface and is missing a member. When a member exists but has the wrong signature you get TS2416 instead, which is more specific.

What to do. Read which member is named. If the interface has changed under you, this error is the list of work to do.

Class X incorrectly implements interface Y

TS2420
interface Serialiser {
  name: string;
  serialise(value: unknown): string;
}

class Broken implements Serialiser {
  serialise(value: unknown): string {
    return String(value);
  }
}

console.log(new Broken().serialise('x'));

What the compiler says

error TS2420: Class 'Broken' incorrectly implements interface 'Serialiser'.
26

TS2416

Lesson 11

Property X in type A is not assignable to the same property in base type B

A member exists but its signature does not line up with the interface or base class. Usually a return type, or a parameter that is narrower than the contract allows.

What to do. Compare the two signatures carefully. A common cause is narrowing a parameter type in an override, which is unsafe for the same reason lesson 06 gave.

Property X in type A is not assignable to the same property in base type B

TS2416
interface Serialiser {
  serialise(value: unknown): string;
}

class Broken implements Serialiser {
  serialise(value: unknown): number {
    return String(value).length;
  }
}

console.log(new Broken().serialise('x'));

What the compiler says

error TS2416: Property 'serialise' in type 'Broken' is not assignable to the same property in base type 'Serialiser'.
27

TS4114

Lesson 11

This member must have an override modifier

noImplicitOverride. You replaced an inherited member without saying so.

What to do. Add override. The payoff comes later: rename the base member and the subclass stops compiling instead of quietly becoming dead code.

This member must have an override modifier

TS4114
class Base {
  greet(): string {
    return 'hello';
  }
}

class Child extends Base {
  greet(): string {
    return 'hi';
  }
}

console.log(new Child().greet());

What the compiler says

error TS4114: This member must have an 'override' modifier because it overrides a member in the base class 'Base'.
28

TS2511

Lesson 11

Cannot create an instance of an abstract class

You called new on an abstract class. It exists to be extended, not instantiated.

What to do. Instantiate a concrete subclass. If there is not one, the class probably wanted to be an interface or a plain class instead.

Cannot create an instance of an abstract class

TS2511
abstract class Shape {
  abstract area(): number;
}

console.log(new Shape().area());

What the compiler says

error TS2511: Cannot create an instance of an abstract class.
29

TS2493

Lesson 07

Tuple type X of length N has no element at index M

You indexed past the end of a tuple. Unlike an array, the compiler knows exactly how long a tuple is.

What to do. Use a real index, or use an array type if the length genuinely varies. This error is a good argument for tuples over arrays where the length is fixed.

Tuple type X of length N has no element at index M

TS2493
type Point = [number, number];

const p: Point = [1, 2];

console.log(p[2]);

What the compiler says

error TS2493: Tuple type 'Point' of length '2' has no element at index '2'.
30

TS4104

Lesson 07

The type is readonly and cannot be assigned to the mutable type

You tried to widen a readonly array into a mutable one, which would hand out permission the original never granted.

What to do. Copy it — [...frozen] — or make the destination readonly too. The direction that is allowed is mutable into readonly, which is why readonly parameters accept everything.

The type is readonly and cannot be assigned to the mutable type

TS4104
const frozen: readonly number[] = [1, 2, 3];

const mutable: number[] = frozen;

console.log(mutable);

What the compiler says

error TS4104: The type 'readonly number[]' is 'readonly' and cannot be assigned to the mutable type 'number[]'.
31

TS2375

Lesson 08

Type undefined is not assignable under exactOptionalPropertyTypes

With exactOptionalPropertyTypes on, x?: string means “absent or a string” and not “or explicitly undefined”. You supplied the explicit undefined.

What to do. Omit the key rather than setting it to undefined. If both really should be allowed, declare it as x?: string | undefined. Object spreads are the usual culprit.

Type undefined is not assignable under exactOptionalPropertyTypes

TS2375
interface Options {
  label?: string;
}

const options: Options = { label: undefined };

console.log(options);

What the compiler says

error TS2375: Type '{ label: undefined; }' is not assignable to type 'Options' with 'exactOptionalPropertyTypes: true'. Consider adding 'undefined' to the types of the target's properties.
32

TS2344

Lesson 16

Type X does not satisfy the constraint Y

A type argument breaks the generic's constraint. Very often a typo in a Pick or a key that is not on the type.

What to do. Check the key spelling. Note that Omit does not produce this error for a bad key — it silently omits nothing, which is why Pick is the safer of the two.

Type X does not satisfy the constraint Y

TS2344
interface User {
  id: number;
  name: string;
}

type Bad = Pick<User, 'nmae'>;

const value = {} as Bad;
console.log(value);

What the compiler says

error TS2344: Type '"nmae"' does not satisfy the constraint 'keyof User'.
33

TS2578

Lesson 18

Unused @ts-expect-error directive

You suppressed an error that is no longer there. This is the feature working: the suppression cannot outlive the problem.

What to do. Delete the comment. This is why @ts-expect-error is strictly better than @ts-ignore — during a migration it leaves you a to-do list that cleans itself up.

Unused @ts-expect-error directive

TS2578
function fine(value: number): string {
  return value.toFixed(2);
}

// @ts-expect-error
const ok = fine(12.5);

console.log(ok);

What the compiler says

error TS2578: Unused '@ts-expect-error' directive.
34

TS7053

Lesson 10

Element implicitly has an any type because expression of type string cannot index

You indexed an object with a general string when its type only has specific keys. Object.keys returns string[], which is the usual source.

What to do. Type the key as keyof T. A small helper — keysOf<T>(o: T): (keyof T)[] — contains the one necessary assertion in a single place, as lesson 10 shows.

Element implicitly has an any type because expression of type string cannot index

TS7053
interface Scores {
  maths: number;
  english: number;
}

const scores: Scores = { maths: 70, english: 65 };

for (const key of Object.keys(scores)) {
  console.log(scores[key]);
}

What the compiler says

error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Scores'.
35

TS2769

Lesson 06

No overload matches this call

A function with several signatures rejected all of them. The message then lists each attempt, which makes it one of the longest errors you will see.

What to do. Read the last overload's complaint first — it is usually the one you meant. Check argument count as well as types, since a missing argument disqualifies every signature at once. Here the fix is to exclude null before calling.

No overload matches this call

TS2769
function keysOf(values: object | null): string[] {
  return Object.keys(values);
}

console.log(keysOf({ a: 1 }));

What the compiler says

error TS2769: No overload matches this call.
36

TS1016

Lesson 06

A required parameter cannot follow an optional parameter

Optional parameters have to come last, because there is no way to supply a later one without the earlier.

What to do. Reorder, or take a single options object. Once a function has more than two optional parameters, the object is almost always the better design anyway.

A required parameter cannot follow an optional parameter

TS1016
function greet(title?: string, name: string): string {
  return title + ' ' + name;
}

console.log(greet('Ada', 'Lovelace'));

What the compiler says

error TS1016: A required parameter cannot follow an optional parameter.
37

TS1064

Lesson 06

The return type of an async function must be Promise<T>

You annotated an async function with a bare value type. Async functions always return a promise.

What to do. Wrap it: Promise<string>. Or drop the annotation entirely and let it be inferred, which is what most code does.

The return type of an async function must be Promise&lt;T&gt;

TS1064
async function fetchTitle(): string {
  return 'Document';
}

console.log(fetchTitle());

What the compiler says

error TS1064: The return type of an async function or method must be the global Promise<T> type. Did you mean to write 'Promise<string>'?
38

TS2554

Lesson 06

Expected N arguments, but got M

Wrong number of arguments. Simple, and worth listing because it is common and because the fix is occasionally not what you expect.

What to do. If you meant to omit one, it needs a ? or a default in the declaration. Note that extra arguments are an error here, unlike plain JavaScript where they are silently ignored.

Expected N arguments, but got M

TS2554
function add(a: number, b: number): number {
  return a + b;
}

console.log(add(1, 2, 3));

What the compiler says

error TS2554: Expected 2 arguments, but got 3.

Errors this page does not cover

Three families, deliberately left out.

When the message genuinely is not enough

Some errors, particularly around generics, produce a wall of text. In rough order of effectiveness:

  1. Annotate an intermediate value. Assign the confusing expression to a const with an explicit type. The error moves to that line and usually gets much shorter.
  2. Hover the pieces. Your editor will tell you what each sub-expression resolved to, which is frequently the whole answer — a type parameter inferred as unknown when you expected something specific.
  3. Simplify until it compiles. Delete half the expression. Then add back one piece at a time. This is bisection, and it works on types exactly as it works on anything else.
  4. Check for distribution. If a conditional type is behaving oddly on a union, lesson 17 explains why, and [T] extends [U] is the fix.

Newsletter

New writing, when it lands.

Occasional notes on shipping software with AI agents. No schedule, no filler, unsubscribe in one click.