This error occurs when a class declares that it implements an interface but is missing one or more required properties or methods. The fix involves adding all missing members or marking interface members as optional.
When you use the `implements` keyword in TypeScript, you're telling the compiler that your class must satisfy the interface contract—meaning it must define all properties and methods that the interface specifies. If your class is missing any of these required members, or if a member has an incompatible type, TypeScript raises the error "Class does not implement interface". This is a compile-time safety feature that prevents mismatches between what code expects and what your class actually provides. This is one of the most common TypeScript errors because it's easy to forget to implement all interface members, especially in interfaces with many properties or when inheriting from interfaces that extend other interfaces.
The error message tells you exactly what's missing. For example:
Class 'UserService' does not implement interface 'IUserService'.
Property 'getName' is missing in type 'UserService' but required in type 'IUserService'.This tells you that:
1. Your class is named UserService
2. It's trying to implement IUserService
3. The getName property/method is missing
Note the exact name and type of the missing member.
Open the interface definition and identify all required members:
interface IUserService {
getName(): string; // Method that returns string
getUserId(): number; // Method that returns number
email: string; // Property
isActive?: boolean; // Optional property
}Pay attention to:
- Method names and their return types
- Property names and types
- Which members are optional (?)
Implement every required member from the interface:
class UserService implements IUserService {
email: string = "";
constructor() {
this.email = "[email protected]";
}
getName(): string {
return "John Doe";
}
getUserId(): number {
return 42;
}
// isActive is optional, so you don't have to implement it
}Make sure:
- Method names match exactly
- Return types match (string returns string, number returns number, etc.)
- Properties have the correct types
Ensure method parameters and return types are correct:
// ❌ WRONG - wrong return type
interface IService {
getValue(): string;
}
class Service implements IService {
getValue(): number { // ERROR: number is not string
return 42;
}
}
// ✅ CORRECT - matching signature
class Service implements IService {
getValue(): string {
return "42";
}
}Parameters must also match:
// ❌ WRONG - missing parameter
interface ILogger {
log(message: string, level: "info" | "error"): void;
}
class Logger implements ILogger {
log(message: string): void { // ERROR: missing level parameter
console.log(message);
}
}
// ✅ CORRECT
class Logger implements ILogger {
log(message: string, level: "info" | "error"): void {
console.log(`[${level}] ${message}`);
}
}Properties and methods must have compatible access levels:
// ❌ WRONG - interface member is public but class member is private
interface IService {
getData(): string; // implicitly public
}
class Service implements IService {
private getData(): string { // ERROR: private doesn't match public
return "data";
}
}
// ✅ CORRECT - both public
class Service implements IService {
public getData(): string { // or just: getData(): string
return "data";
}
}Note: You can't make a class member more restrictive (private/protected) than the interface declares it.
If you don't need to implement every member, mark them as optional in the interface:
interface IService {
name: string;
getValue(): number;
cache?: Map<string, any>; // Optional property
reset?(): void; // Optional method
}
class MinimalService implements IService {
name = "Service";
getValue() {
return 42;
}
// cache and reset are optional, so they don't need to be implemented
}However, it's usually better to create separate, smaller interfaces for different use cases rather than having large interfaces with many optional members.
When a class implements multiple interfaces or when interfaces extend other interfaces, make sure you implement everything:
interface IBase {
id: number;
}
interface IExtended extends IBase {
name: string;
}
// ❌ WRONG - missing id (from IBase)
class Model implements IExtended {
name: string = "";
}
// ✅ CORRECT - implements all members from the chain
class Model implements IExtended {
id: number = 0;
name: string = "";
}Alternatively, a class can implement both explicitly:
// ✅ Also correct
class Model implements IBase, IExtended {
id: number = 0;
name: string = "";
}### Using Satisfies Operator (TypeScript 4.9+)
The satisfies operator can help catch interface mismatches without explicitly declaring implements:
const service = {
getName: () => "John",
getValue: () => 42,
} satisfies IService;This checks the object against the interface without the overhead of creating a class.
### Structural vs Nominal Typing
TypeScript uses "structural typing"—it checks that an object has the right shape, not just that it declares implements. An object can satisfy an interface without explicitly declaring it:
interface Duck {
quack(): void;
swim(): void;
}
const mallard = {
quack() { console.log("quack!"); },
swim() { console.log("swimming!"); },
};
// mallard satisfies Duck structurally, even without "implements"However, using explicit implements gives you better IDE support and earlier error detection.
### Narrowing Interface Implementations
For large interfaces, consider splitting into smaller, focused interfaces:
// ❌ Large interface with many unrelated methods
interface IUserService {
getUser(): User;
updateUser(user: User): void;
deleteUser(id: string): void;
sendEmail(to: string, message: string): void;
logActivity(action: string): void;
}
// ✅ Better: separate concerns
interface IUserRepository {
getUser(): User;
updateUser(user: User): void;
deleteUser(id: string): void;
}
interface INotificationService {
sendEmail(to: string, message: string): void;
}
interface IAuditService {
logActivity(action: string): void;
}
class UserService implements IUserRepository, INotificationService, IAuditService {
// Clearer what each method is responsible for
}This makes it easier to implement interfaces and follow the Single Responsibility Principle.
### Abstract Classes vs Interfaces
Abstract classes are similar but allow implementation:
abstract class BaseService {
abstract getData(): string; // Must be implemented by subclass
processData() { // Can have default implementation
const data = this.getData();
return data.toUpperCase();
}
}
class Service extends BaseService {
getData() {
return "hello";
}
}Abstract classes are useful when you want to share implementation code, while interfaces are purely structural contracts.
Function expression requires a return type
Function expression requires a return type
Value of type 'string | undefined' is not iterable
How to fix "Value is not iterable" in TypeScript
Type 'undefined' is not assignable to type 'string'
How to fix "Type undefined is not assignable to type string" in TypeScript
Type narrowing from typeof check produces 'never'
How to fix "Type narrowing produces never" in TypeScript
Type parameter 'T' has conflicting constraints
How to fix "Type parameter has conflicting constraints" in TypeScript