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.
Element implicitly has an any type because expression of type string cannot index — lesson 10
TS2322
Lesson 02Type 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 Level = 'debug' | 'info';
const level: Level = 'warn';
console.log(level);
What the compiler says
error TS2322: Type '"warn"' is not assignable to type 'Level'.
TS2345
Lesson 06Argument 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.
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'.
TS2339
Lesson 03Property 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.
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'.
TS2551
Lesson 03Property 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.
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'?
TS2741
Lesson 03Property 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.
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'.
TS2353
Lesson 03Object 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.
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'.
TS7006
Lesson 01Parameter 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.
const shout = (word) => word.toUpperCase();
console.log(shout('hello'));
What the compiler says
error TS7006: Parameter 'word' implicitly has an 'any' type.
TS18047
Lesson 08X 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.
function initial(name: string | null): string {
return name.charAt(0);
}
console.log(initial('Ada'));
What the compiler says
error TS18047: 'name' is possibly 'null'.
TS18048
Lesson 08X 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.
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'.
TS2532
Lesson 07Object 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.
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'.
TS18046
Lesson 12X 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.
try {
throw new Error('failed');
} catch (error) {
console.log(error.message);
}
What the compiler says
error TS18046: 'error' is of type 'unknown'.
TS2366
Lesson 04Function 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.
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'.
TS2540
Lesson 03Cannot 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.
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.
TS2739
Lesson 03Type 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.
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
TS2352
Lesson 13Conversion 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.
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.
TS2367
Lesson 04This 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.
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.
TS2693
Lesson 01X 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.
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.
TS2749
Lesson 10X 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.
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'?
TS2552
Lesson 15Cannot 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.
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'?
TS2304
Lesson 15Cannot 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.
function save(data: string): void {
frobnicator.setItem('key', data);
}
save('x');
What the compiler says
error TS2304: Cannot find name 'frobnicator'.
TS2307
Lesson 15Cannot 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.
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.
TS2454
Lesson 08Variable 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.
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.
TS2564
Lesson 18Property 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.
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.
TS2341
Lesson 11Property 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 #.
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'.
TS2420
Lesson 11Class 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.
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'.
TS2416
Lesson 11Property 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.
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'.
TS4114
Lesson 11This 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.
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'.
TS2511
Lesson 11Cannot 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.
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.
TS2493
Lesson 07Tuple 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.
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'.
TS4104
Lesson 07The 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.
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[]'.
TS2375
Lesson 08Type 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.
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.
TS2344
Lesson 16Type 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.
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'.
TS2578
Lesson 18Unused @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.
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.
TS7053
Lesson 10Element 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.
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'.
TS2769
Lesson 06No 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.
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.
TS1016
Lesson 06A 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.
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.
TS1064
Lesson 06The 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.
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>'?
TS2554
Lesson 06Expected 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.
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.
- Configuration errors (the TS5xxx and TS6xxx ranges) — bad
tsconfigoptions, unresolvable paths, conflicting flags. These are about your setup rather than your code, and the messages are usually self-explanatory. The tsconfig page covers the settings. - Syntax errors (most of TS1xxx) — a missing brace is a missing brace, and your editor will show it before the compiler does.
- Errors from inside dependencies — these come from somebody else's
.d.ts.skipLibCheckexists to silence them, and it is a reasonable default; when one leaks through anyway, lesson 15 covers augmenting a library's types.
When the message genuinely is not enough
Some errors, particularly around generics, produce a wall of text. In rough order of effectiveness:
- Annotate an intermediate value. Assign the confusing expression to a
constwith an explicit type. The error moves to that line and usually gets much shorter. - 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
unknownwhen you expected something specific. - 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.
- Check for distribution. If a conditional type is behaving oddly on a union, lesson 17 explains why, and
[T] extends [U]is the fix.