How to fix Class 'X' incorrectly implements interface 'Y' in TypeScript

TypeScriptBEGINNERMEDIUMUpdated Dec 8, 2025· 106 views

This error occurs when a class implements an interface but one or more of its properties or methods have incompatible types or signatures. The fix involves aligning the types, signatures, and access modifiers between the class and interface.

Quick fix

Fixes TS2420

Class 'UserService' incorrectly implements interface 'IUserService'.
  Types of property 'getUser' are incompatible.
    Type '() => User | null' is not assignable to type '() => User'.
      Type 'null' is not assignable to type 'User'.
  1. Read the detailed error message to identify the incompatible property
  2. Compare the interface definition with your class implementation
  3. Fix property type mismatches
  4. Align method signatures exactly
typescript — error
Class 'UserService' incorrectly implements interface 'IUserService'.
Types of property 'getUser' are incompatible.
Type '() => User | null' is not assignable to type '() => User'.
Type 'null' is not assignable to type 'User'.
Class 'X' incorrectly implements interface 'Y'

What this error means

When you declare that a class implements an interface in TypeScript, you're creating a contract. The `implements` clause checks that your class satisfies the interface's shape—but it goes further than just checking that properties exist. The "Class incorrectly implements interface" error means that while your class might have the right property or method names, their types, signatures, or accessibility don't match what the interface requires. This could be a return type mismatch, parameter differences, incompatible property types, or access modifier conflicts. TypeScript's type checker uses structural typing to verify that the class implementation is compatible with the interface contract. This error is your early warning system that prevents runtime type mismatches and helps maintain type safety across your codebase.

Typical symptoms

  • TypeScript compiler shows TS2420 error on the class declaration line
  • Error message specifies which property has type incompatibility
  • IDE shows red squiggles under the class name or implements clause
  • Build fails during compilation before any code execution
  • Error details show 'Types of property X are incompatible' or similar
  • Works at runtime in plain JavaScript but fails TypeScript compilation

Common causes

  1. Property type in class doesn't match the type specified in the interface (most common)
  2. Method return type differs from the interface signature
  3. Method parameters have different types, count, or optionality than interface requires
  4. Class property is private or protected but interface expects it to be public
  5. Property is readonly in interface but not in class, or vice versa
  6. Generic type parameters are incompatible or incorrectly constrained
  7. Class extends another class with private/protected members that conflict with the interface

How to fix "Class 'X' incorrectly implements interface 'Y'"

1Read the detailed error message to identify the incompatible property

TypeScript provides specific error details about what's wrong. For example:

bash
Class 'UserService' incorrectly implements interface 'IUserService'.
  Types of property 'getUser' are incompatible.
    Type '() => User | null' is not assignable to type '() => User'.
      Type 'null' is not assignable to type 'User'.

This tells you:
1. The class is UserService
2. The problematic property is getUser
3. The class returns User | null but the interface expects User
4. The specific issue is that null isn't acceptable

Focus on the property name and type mismatch details.

2Compare the interface definition with your class implementation

Open both files side by side and check the problematic property:

typescript
// Interface definition
interface IUserService {
  getUser(): User;           // Returns User (not nullable)
  email: string;             // string type
  age: number;               // number type
}

// Class implementation
class UserService implements IUserService {
  email: string = "";
  age: string = "25";        // ❌ WRONG: string should be number

  getUser(): User | null {   // ❌ WRONG: should return User, not User | null
    return null;
  }
}

Look for:
- Different types (string vs number, User vs User | null)
- Different signatures (parameters, return types)
- Missing readonly/optional modifiers

3Fix property type mismatches

Ensure all property types match exactly:

typescript
// ❌ WRONG - type mismatch
interface IConfig {
  port: number;
  enabled: boolean;
}

class Config implements IConfig {
  port: string = "3000";      // ERROR: string is not number
  enabled: number = 1;        // ERROR: number is not boolean
}

// ✅ CORRECT - types match
class Config implements IConfig {
  port: number = 3000;
  enabled: boolean = true;
}

If you need nullable types, update the interface:

typescript
interface IUserService {
  getUser(): User | null;    // Now allows null
}

class UserService implements IUserService {
  getUser(): User | null {   // ✅ Now matches
    return null;
  }
}
4Align method signatures exactly

Method parameters and return types must match precisely:

typescript
// ❌ WRONG - return type mismatch
interface ILogger {
  log(message: string): void;
}

class Logger implements ILogger {
  log(message: string): boolean {  // ERROR: returns boolean, not void
    console.log(message);
    return true;
  }
}

// ✅ CORRECT - void return
class Logger implements ILogger {
  log(message: string): void {
    console.log(message);
  }
}

Parameter mismatches also cause errors:

typescript
// ❌ WRONG - parameter type differs
interface ICalculator {
  add(a: number, b: number): number;
}

class Calculator implements ICalculator {
  add(a: string, b: string): number {  // ERROR: params should be number
    return parseInt(a) + parseInt(b);
  }
}

// ✅ CORRECT
class Calculator implements ICalculator {
  add(a: number, b: number): number {
    return a + b;
  }
}
5Fix access modifier conflicts

Interface members are public by default. Class members can't be more restrictive:

typescript
// ❌ WRONG - private member on public interface property
interface IService {
  getData(): string;  // implicitly public
}

class Service implements IService {
  private getData(): string {  // ERROR: can't be private
    return "data";
  }
}

// ✅ CORRECT - make it public
class Service implements IService {
  public getData(): string {   // or just: getData(): string
    return "data";
  }
}

Protected members also cause this error:

typescript
// ❌ WRONG
class Service implements IService {
  protected getData(): string {  // ERROR: can't be protected
    return "data";
  }
}
6Handle readonly property mismatches

The readonly modifier must match between interface and class:

typescript
// ❌ WRONG - readonly mismatch
interface IConfig {
  readonly apiKey: string;
}

class Config implements IConfig {
  apiKey: string = "key123";  // ERROR: missing readonly
}

// ✅ CORRECT
class Config implements IConfig {
  readonly apiKey: string = "key123";
}

Alternatively, if the interface doesn't need readonly, remove it:

typescript
interface IConfig {
  apiKey: string;  // not readonly
}

class Config implements IConfig {
  apiKey: string = "key123";  // ✅ matches
}
7Verify optional property consistency

Optional properties (?) in interfaces should match the class:

typescript
// ❌ WRONG - optional mismatch
interface IUser {
  name: string;
  email?: string;  // optional
}

class User implements IUser {
  name: string = "";
  email: string = "";  // Not optional in class - this is actually OK
}

// This is fine because the class can be more specific than the interface

However, you can't make required properties optional:

typescript
// ❌ WRONG - required property made optional
interface IUser {
  name: string;  // required
}

class User implements IUser {
  name?: string;  // ERROR: can't make it optional
}

// ✅ CORRECT
class User implements IUser {
  name: string = "";
}
8Fix generic type parameter issues

Generic constraints must be compatible:

typescript
// ❌ WRONG - incompatible generic types
interface IRepository<T> {
  save(item: T): void;
}

class UserRepository implements IRepository<User> {
  save(item: string): void {  // ERROR: expects User, got string
    // ...
  }
}

// ✅ CORRECT
class UserRepository implements IRepository<User> {
  save(item: User): void {
    // ...
  }
}

Or with bounded generics:

typescript
interface IProcessor<T extends object> {
  process(data: T): void;
}

// ❌ WRONG - string doesn't extend object
class StringProcessor implements IProcessor<string> {
  process(data: string): void { }
}

// ✅ CORRECT - User extends object
class UserProcessor implements IProcessor<User> {
  process(data: User): void { }
}

Advanced notes

### Private/Protected Members in Extended Interfaces

When an interface extends a class with private or protected members, only that class or its subclasses can implement the interface:

typescript
class Control {
  private state: any;
}

interface SelectableControl extends Control {
  select(): void;
}

// ❌ ERROR - SelectableControl requires private 'state' from Control
class Image implements SelectableControl {
  select() { }
}

// ✅ CORRECT - extends Control, inherits private state
class Button extends Control implements SelectableControl {
  select() { }
}

This pattern is used to restrict who can implement an interface.

### Bivariance and Method Signatures

TypeScript allows method parameter bivariance for flexibility, but this can sometimes cause unexpected compatibility:

typescript
interface IHandler {
  handle(event: MouseEvent): void;
}

class Handler implements IHandler {
  // This is technically allowed (contravariance)
  handle(event: Event): void {
    console.log(event);
  }
}

For stricter checking, enable strictFunctionTypes in tsconfig.json.

### Index Signatures

Classes implementing interfaces with index signatures must include them:

typescript
interface StringMap {
  [key: string]: string;
}

// ❌ ERROR - missing index signature
class Config implements StringMap {
  apiKey: string = "key";
}

// ✅ CORRECT
class Config implements StringMap {
  [key: string]: string;
  apiKey: string = "key";
}

### Constructor Signatures

Interfaces can't check constructor signatures via implements. Constructors are on the static side of the class:

typescript
interface ClockConstructor {
  new (hour: number, minute: number): ClockInterface;
}

interface ClockInterface {
  tick(): void;
}

// ❌ ERROR - constructor isn't checked by implements
class Clock implements ClockConstructor {
  constructor(h: number, m: number) { }
  tick() { }
}

// ✅ Use a factory function instead
function createClock(
  ctor: ClockConstructor,
  hour: number,
  minute: number
): ClockInterface {
  return new ctor(hour, minute);
}

### Type Assertions vs Implements

You can use type assertions to bypass compile-time checks, but this removes safety:

typescript
interface IUser {
  name: string;
  email: string;
}

// ❌ Unsafe - no type checking
const user = {
  name: "John"
  // email is missing!
} as IUser;

// ✅ Better - use implements for compile-time safety
class User implements IUser {
  name: string = "";
  email: string = "";  // Compiler forces you to add this
}

Always prefer implements for classes to get full type-checking benefits.

Frequently asked questions

What does "TS2420" mean?
When you declare that a class implements an interface in TypeScript, you're creating a contract. The implements clause checks that your class satisfies the interface's shape—but it goes further than just checking that properties exist. The "Class incorrectly implements interface" error means that while your class might have the right property or method names, their types, signatures, or accessibility don't match what the interface requires.
How do I fix "TS2420"?
This error occurs when a class implements an interface but one or more of its properties or methods have incompatible types or signatures. The fix involves aligning the types, signatures, and access modifiers between the class and interface.
What causes "TS2420"?
Property type in class doesn't match the type specified in the interface (most common) Method return type differs from the interface signature Method parameters have different types, count, or optionality than interface requires
Is the "TS2420" error serious?
It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.

Official resources & further reading