This TypeScript error occurs when a non-abstract (concrete) class extends an abstract base class but fails to implement all abstract members. All classes that extend abstract classes must either implement every abstract method and property, or be declared abstract themselves.
The "Cannot extend non-abstract class with abstract members" error appears when you create a regular class that extends an abstract class, but you don't provide implementations for all the abstract methods or properties defined in the base class. Abstract classes in TypeScript serve as blueprints that define a contract. When you mark a method or property as abstract, you're declaring that any concrete (non-abstract) subclass must provide its own implementation. If a class extends an abstract class without implementing all abstract members, TypeScript raises this error to enforce the contract. This is a fundamental principle of object-oriented programming: abstract members exist to ensure that all concrete instances have the required functionality. TypeScript enforces this at compile time, preventing runtime errors where you might try to call a method that doesn't exist. The error typically manifests as "Non-abstract class 'ClassName' does not implement inherited abstract member 'methodName' from class 'BaseClassName'." This clearly identifies which abstract members are missing implementations.
Read the full error message to see exactly which members need implementation:
// Example error message:
// Non-abstract class 'Dog' does not implement inherited
// abstract member 'makeSound' from class 'Animal'
abstract class Animal {
abstract makeSound(): string;
abstract age: number;
}
// This will cause the error:
class Dog extends Animal {
// Missing: makeSound() and age
}TypeScript will list all missing members. Note them down before proceeding to fix.
Add implementations for each abstract method with the exact signature:
abstract class Animal {
abstract makeSound(): string;
abstract move(distance: number): void;
}
// CORRECT - All abstract methods implemented
class Dog extends Animal {
makeSound(): string {
return "Woof!";
}
move(distance: number): void {
console.log(`Dog moved ${distance} meters`);
}
}
// Now you can instantiate:
const dog = new Dog();
console.log(dog.makeSound()); // "Woof!"
dog.move(10);Ensure:
- Method names match exactly (case-sensitive)
- Parameter types match the abstract declaration
- Return types match or are compatible
- Access modifiers are compatible (can't make public method private)
Abstract properties must also be defined in the concrete class:
abstract class Vehicle {
abstract wheels: number;
abstract readonly brand: string;
}
// CORRECT implementation
class Car extends Vehicle {
wheels: number = 4;
readonly brand: string = "Toyota";
// Or initialize in constructor:
constructor(brand: string) {
super();
this.brand = brand;
}
}
// WRONG - missing property implementations
class Motorcycle extends Vehicle {
wheels: number = 2;
// Error: missing 'brand' property
}Properties can be initialized:
- Directly in the class body
- In the constructor
- As constructor parameters with access modifiers
If you think you implemented the method but still get the error, check the signature:
abstract class Shape {
abstract calculateArea(): number;
abstract draw(context: CanvasRenderingContext2D): void;
}
// WRONG - signature mismatch
class Circle extends Shape {
// Wrong return type (void instead of number)
calculateArea(): void {
console.log("Calculating...");
}
// Wrong parameter type
draw(context: any): void {
// ...
}
}
// CORRECT - signatures match
class Rectangle extends Shape {
width: number;
height: number;
constructor(width: number, height: number) {
super();
this.width = width;
this.height = height;
}
calculateArea(): number {
return this.width * this.height;
}
draw(context: CanvasRenderingContext2D): void {
context.fillRect(0, 0, this.width, this.height);
}
}Use your IDE's autocomplete after typing "override" to ensure correct signatures.
If you're building a class hierarchy and aren't ready to implement all members, mark the derived class as abstract too:
abstract class Animal {
abstract makeSound(): string;
abstract eat(food: string): void;
}
// Intermediate abstract class - doesn't implement everything
abstract class Mammal extends Animal {
// Implements only one abstract member
eat(food: string): void {
console.log(`Eating ${food}`);
}
// makeSound() still abstract - concrete subclasses must implement
}
// Now concrete class only needs to implement remaining abstract members
class Cat extends Mammal {
makeSound(): string {
return "Meow";
}
// eat() is inherited from Mammal
}
const cat = new Cat();
cat.eat("fish"); // From Mammal
console.log(cat.makeSound()); // From CatThis is useful for creating layered class hierarchies where some behavior is shared across a group of subclasses.
TypeScript 4.3+ supports the override keyword to make implementation intent explicit:
abstract class Base {
abstract process(): void;
// Non-abstract method
initialize(): void {
console.log("Initializing...");
}
}
class Derived extends Base {
// Implementing abstract method
override process(): void {
console.log("Processing...");
}
// Overriding non-abstract method
override initialize(): void {
super.initialize();
console.log("Extra initialization");
}
}Enable in tsconfig.json:
{
"compilerOptions": {
"noImplicitOverride": true
}
}This makes TypeScript require override for all overridden members, catching typos where you think you're implementing an abstract member but actually created a new method.
### Abstract Classes vs Interfaces
Abstract classes and interfaces both define contracts, but have key differences:
Abstract classes:
- Can have both abstract and concrete members
- Can have constructors, fields, and implementation logic
- Support access modifiers (private, protected, public)
- Single inheritance only (a class can extend one abstract class)
abstract class AbstractLogger {
protected logLevel: string = "info";
abstract log(message: string): void;
// Concrete helper method
formatMessage(msg: string): string {
return `[${this.logLevel}] ${msg}`;
}
}Interfaces:
- Only define method/property signatures
- No implementation or state
- Multiple inheritance (a class can implement many interfaces)
- All members are implicitly public
interface ILogger {
log(message: string): void;
level: string;
}When to use abstract classes:
- You need to share implementation code across subclasses
- You need to maintain state or have constructor logic
- You want to use protected/private members
- You're modeling "is-a" relationships
When to use interfaces:
- You need multiple inheritance
- You're defining a pure contract with no implementation
- You're modeling "can-do" capabilities
- You want maximum flexibility for implementers
### Partial Implementation Pattern
You can combine abstract classes with partial implementation:
abstract class DataStore {
// Abstract - must be implemented
abstract connect(): Promise<void>;
abstract disconnect(): Promise<void>;
abstract query(sql: string): Promise<any>;
// Concrete - shared across all implementations
private isConnected: boolean = false;
async ensureConnected(): Promise<void> {
if (!this.isConnected) {
await this.connect();
this.isConnected = true;
}
}
// Template method pattern
async executeQuery(sql: string): Promise<any> {
await this.ensureConnected();
return this.query(sql);
}
}
class PostgresStore extends DataStore {
async connect(): Promise<void> {
// Postgres-specific connection
}
async disconnect(): Promise<void> {
// Postgres-specific disconnection
}
async query(sql: string): Promise<any> {
// Postgres-specific query
}
// Inherits ensureConnected() and executeQuery()
}This pattern (Template Method) lets you define the algorithm structure in the abstract class while letting subclasses customize specific steps.
### Abstract Static Members
TypeScript 4.9+ introduced abstract static members:
abstract class Model {
abstract id: number;
// Abstract static method
static abstract fromJSON(data: any): Model;
// Regular abstract instance method
abstract save(): Promise<void>;
}
class User extends Model {
id: number;
name: string;
constructor(id: number, name: string) {
super();
this.id = id;
this.name = name;
}
// Must implement static method
static fromJSON(data: any): User {
return new User(data.id, data.name);
}
// Must implement instance method
async save(): Promise<void> {
// Save logic
}
}
// Usage:
const user = User.fromJSON({ id: 1, name: "Alice" });
await user.save();This enforces that all subclasses implement the same static factory methods or utilities.
### Constructor Signatures in Abstract Classes
Abstract classes can define constructor signatures that subclasses must follow:
abstract class Component {
protected props: any;
constructor(props: any) {
this.props = props;
}
abstract render(): HTMLElement;
}
class Button extends Component {
// Must call super() with props
constructor(props: any) {
super(props);
}
render(): HTMLElement {
const button = document.createElement("button");
button.textContent = this.props.label;
return button;
}
}Subclass constructors must call super() before accessing this.
### Type-Safe Abstract Factory Pattern
Combine abstract classes with generics for type-safe factories:
abstract class Factory<T> {
abstract create(): T;
createMultiple(count: number): T[] {
return Array(count).fill(null).map(() => this.create());
}
}
class UserFactory extends Factory<User> {
create(): User {
return new User(
Math.random(),
`User${Math.floor(Math.random() * 1000)}`
);
}
}
const factory = new UserFactory();
const users: User[] = factory.createMultiple(5);This ensures type safety across the factory hierarchy.
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