abstract
Marks a class that cannot be instantiated, or a member that subclasses must implement.
abstract class Name {
abstract property: Type; // no initializer allowed
abstract get accessor(): Type; // no body allowed
abstract method(arg: Type): Return; // no body allowed
concreteMethod(): void { } // implementations are allowed too
}
// Abstract construct signature (TypeScript 4.2+):
let Ctor: abstract new () => Name;- Added in TypeScript 1.6
Parameters
| Parameter | Type | Description |
|---|---|---|
$abstract class | modifier | Prevents the class from being instantiated with new. It may still contain fully implemented members and a constructor for subclasses to call via super. |
$abstract memberoptional | modifier | Declares a property, accessor or method with no implementation. Only legal inside an abstract class; abstract properties cannot have initializers and abstract methods cannot have bodies. |
$abstract new () => Toptional | construct signature modifier | Marks a construct signature as accepting abstract constructors, so an abstract class can be passed to a function that only extends it rather than instantiating it. |
Errors
Errors at compile time with 'Cannot create an instance of an abstract class' on new, with 'Non-abstract class does not implement inherited abstract member' when a subclass omits one, and when an abstract class is assigned to a non-abstract construct signature.
Changelog
- PHP 2.0Abstract properties and accessors were added, extending abstract beyond methods.
- PHP 4.2`abstract new () => T` construct signatures were added, so a function can accept an abstract class it intends to subclass rather than instantiate.
An abstract class is a base that is deliberately incomplete. It can hold real implementations, real state and a constructor, but it declares some members without bodies and cannot be instantiated until a subclass fills them in.
Abstract class versus interface
Reach for an interface when you only need a shape. Reach for an abstract class when the base must carry code or state — a template method that calls into abstract steps, a constructor that validates shared invariants, protected helpers subclasses reuse. An interface gives you none of that; an abstract class gives you all of it, at the cost of a single-inheritance slot.
What trips people up
- The keyword is fully erased. Nothing prevents instantiation at runtime — the emitted class is an ordinary one, and plain JavaScript can
newit happily. The guarantee is only as strong as your type checking. - Passing the class itself fails.
typeof Baseis not assignable tonew () => Base, because that would let the callee instantiate it. Useabstract new () => Basewhen the function only intends to subclass. - Abstract properties have no initializer, and under
useDefineForClassFieldsa subclass field is defined aftersuper()returns — so a base constructor that reads an abstract property still seesundefined.
Example
When to use it
- Define a base command class where run() is shared but validate() must be supplied per command.
- Share constructor validation across a family of domain entities.
- Force every subclass to declare a discriminant property used for narrowing.
- Accept a base class in a mixin factory that only ever extends it.
Frequently asked questions
What is the difference between an abstract class and an interface?
An abstract class can contain implementations, fields, a constructor and protected members, and a class may extend only one of them; an interface is pure type information with no runtime presence and a class may implement many. Choose an abstract class when subclasses should inherit shared code or state, and an interface when you only need to describe a shape.
Why can I still instantiate an abstract class at runtime?
Because abstract is erased at compile time — the emitted JavaScript is an ordinary class with no instantiation guard. The restriction is enforced only by the type checker, so untyped JavaScript, a cast to any, or a reflective call can create an instance whose abstract members are simply missing.
How do I pass an abstract class as a parameter in TypeScript?
Type the parameter as `abstract new (...args: any[]) => T`, added in TypeScript 4.2. An ordinary `new () => T` signature is rejected for an abstract class because it would allow the callee to instantiate it; the abstract form permits both abstract and concrete constructors for cases like mixin factories that only extend.
More examples
The template method pattern
render() is concrete and controls the sequence, while header() and rows() are abstract and protected, so they are implementation details a subclass must supply but outside callers cannot invoke. footer() shows the third category: a concrete member with a usable default. An interface could not express any of this, because it cannot carry the render() implementation.
abstract class Report {
// The template: fixed order, subclasses fill in the steps.
render(): string {
return [this.header(), ...this.rows(), this.footer()].join("\n");
}
protected abstract header(): string;
protected abstract rows(): string[];
protected footer(): string {
return "-- end --"; // a default subclasses may override
}
}
class SalesReport extends Report {
protected header() { return "REGION | TOTAL"; }
protected rows() { return ["EU | 120", "US | 340"]; }
}
console.log(new SalesReport().render());
// REGION | TOTAL
// EU | 120
// US | 340
// -- end --Abstract construct signatures
The abstract modifier on the construct signature says the function will subclass the constructor rather than call it, which is exactly what a mixin factory does. Without it, TypeScript refuses to pass Shape because the ordinary signature would permit `new Ctor()` on an abstract class. The returned subclass is concrete, so it can be instantiated normally.
abstract class Shape {
abstract getArea(): number;
}
interface HasArea {
getArea(): number;
}
// Wrong: promises the callee may instantiate Ctor.
// function mixin(Ctor: new () => HasArea) { ... }
// mixin(Shape); // Cannot assign an abstract constructor type
// // to a non-abstract constructor type.
// Right: signals "I will only extend it".
function withDefaultArea(Ctor: abstract new () => HasArea) {
return class extends Ctor {
getArea() { return 42; }
};
}
const Concrete = withDefaultArea(Shape);
console.log(new Concrete().getArea());
// 42
Discussion