This TypeScript error occurs when a derived class attempts to override a method from its base class with an incompatible signature. The method in the derived class must be assignable to the base class method, meaning it should have compatible parameter types and return type. Fixing this involves ensuring method signatures match or using appropriate type annotations.
In TypeScript, when a class extends another class, it inherits all methods from the base class. If the derived class defines a method with the same name as a method in the base class, it overrides that method. However, TypeScript enforces strict compatibility rules for method overriding: the overriding method must be assignable to the base method. This means the parameter types should be compatible (contravariant), and the return type should be compatible (covariant). The error occurs when TypeScript detects that the derived class method signature is not compatible with the base class method signature, which could lead to runtime errors if the derived class is used through a base class reference.
Look at the error message to identify which method is causing the issue. Compare the method signature in the base class with the one in the derived class. Check parameter types, return type, and optional/required status.
// Base class
class Animal {
makeSound(volume: number): string {
return `Sound at volume ${volume}`;
}
}
// Derived class with incompatible override
class Dog extends Animal {
// ERROR: Parameter type mismatch (string vs number)
makeSound(volume: string): string {
return `Bark at volume ${volume}`;
}
}The return type of the derived method must be assignable to the return type of the base method. You can return a more specific type (subtype) but not a more general type.
class Base {
getValue(): Animal {
return new Animal();
}
}
class Derived extends Base {
// OK: Dog is a subtype of Animal
getValue(): Dog {
return new Dog();
}
}
class WrongDerived extends Base {
// ERROR: Object is not assignable to Animal
getValue(): Object {
return {};
}
}Parameter types in the derived method should be assignable from the base method parameters. You can accept more general types (supertypes) but not more specific types.
class Base {
process(animal: Animal): void {
console.log(animal.name);
}
}
class Derived extends Base {
// OK: Object is a supertype of Animal
process(thing: Object): void {
console.log(thing.toString());
}
}
class WrongDerived extends Base {
// ERROR: Dog is a subtype, not supertype
process(dog: Dog): void {
console.log(dog.bark());
}
}You cannot make an optional parameter required in the derived class. The derived method can have fewer required parameters (by making them optional) but not more.
class Base {
// Optional parameter
configure(options?: object): void {
// implementation
}
}
class Derived extends Base {
// ERROR: Made optional parameter required
configure(options: object): void {
// implementation
}
}
class CorrectDerived extends Base {
// OK: Still optional, can add default
configure(options: object = {}): void {
// implementation
}
}If you need to support multiple signatures, use method overloading. The implementation signature must be compatible with all overloads.
class Base {
process(input: string): string;
process(input: number): number;
process(input: string | number): string | number {
if (typeof input === "string") {
return input.toUpperCase();
} else {
return input * 2;
}
}
}
class Derived extends Base {
// Must support all base overloads
process(input: string): string;
process(input: number): number;
process(input: string | number): string | number {
// Can add new behavior but must be compatible
if (typeof input === "string") {
return `Processed: ${input.toUpperCase()}`;
} else {
return input * 3;
}
}
}If methods use generic types, ensure the constraints are compatible. The derived method can have tighter constraints but not looser ones.
class Base {
process<T extends Animal>(item: T): T {
return item;
}
}
class Derived extends Base {
// OK: Dog extends Animal
process<T extends Dog>(item: T): T {
return item;
}
}
class WrongDerived extends Base {
// ERROR: T extends Object is looser than T extends Animal
process<T extends Object>(item: T): T {
return item;
}
}In rare cases where you know the override is safe but TypeScript cannot verify it, you can use type assertions. Use this cautiously as it bypasses type safety.
class Base {
process(value: string): string {
return value;
}
}
class Derived extends Base {
// Type assertion to suppress error (use with caution)
process(value: any): string {
return String(value);
}
}
// Better alternative: use a union type
class BetterDerived extends Base {
process(value: string | number): string {
return String(value);
}
}TypeScript's method compatibility rules follow the Liskov Substitution Principle (LSP), which states that objects of a superclass should be replaceable with objects of a subclass without affecting program correctness. The contravariant parameter rule and covariant return rule ensure this principle. When debugging these errors, remember that TypeScript uses structural typing, so compatibility is based on shape, not nominal inheritance. For complex inheritance hierarchies, consider using composition over inheritance or the Strategy pattern. The override keyword (available in TypeScript 4.3+) can help catch errors by explicitly marking method overrides, though it doesn't change the compatibility rules.
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