In TypeScript, what does the `abstract` modifier mean when it is written on a class and when it is written on one of that class's members, and which errors does the compiler raise around it?
answer
- incomplete on purpose
- class: base only, never new
- member: signature, no body
- concrete subclass must fill every hole
- not with static, not with private
basics
~20 sabstract on a class blocks creating instances of that class directly. abstract on a member declares a signature with no body that every concrete subclass must supply. TypeScript rejects instantiating the class and rejects a non-abstract subclass that leaves a member unimplemented.
solid answer
~50 s`abstract` marks a declaration as deliberately incomplete. On a class it means "base only": writing `new Shape()` fails with *Cannot create an instance of an abstract class*, though the class can still hold fields, a constructor, and fully implemented methods that subclasses inherit. On a member — a method, a property, or an accessor — it means the member is part of the instance type but this class supplies no code: `abstract area(): number;` has a signature and no body. Any non-abstract subclass then has to implement every inherited abstract member, or you get *Non-abstract class 'Circle' does not implement inherited abstract member 'area' from class 'Shape'*. An abstract member may only appear inside an abstract class, may not have a body, and may not be combined with `static` or `private` — `protected abstract` is fine. All of this is checked at compile time; the modifier is erased from the emitted JavaScript.
code
typescript · 22 linesabstract class Shape {
readonly name: string;
constructor(name: string) {
this.name = name;
}
describe(): string {
return `${this.name} has area ${this.area()}`;
}
abstract area(): number;
}
class Square extends Shape {
constructor(private side: number) {
super('square');
}
area(): number {
return this.side * this.side;
}
}
const shapes: Shape[] = [new Square(3)];
console.log(shapes[0].describe());go deeper
Be able to write an abstract class with one abstract method and a subclass that implements it, and to say plainly that you cannot call new on the abstract class itself.
Explain the exact compiler errors and their triggers, that abstract members carry no body, that only an abstract subclass may leave holes open, and why static abstract and private abstract are rejected.
Show the template-method shape in real code: shared algorithm in the base, mandatory hooks as abstract members, and be ready to say what the compile-time guarantee does and does not cover at the boundaries of your codebase.
Own the call on whether a mandatory-hook base class is the right extension point for a library at all, given that it consumes the single extends slot and couples every implementer to your base's evolution.
## The one-line model `abstract` marks a declaration as **deliberately incomplete**. On a class it says: this exists to be extended, never instantiated on its own. On a member it says: this member belongs to the instance type, but this class does not provide the code — a concrete subclass must. Both statements are enforced by the type checker only; the modifier itself does not survive into the emitted JavaScript. ## `abstract` on the class ```ts abstract class Shape { readonly name: string; constructor(name: string) { this.name = name; } describe(): string { return `${this.name} has area ${this.area()}`; } abstract area(): number; } const s = new Shape('blob'); // Error: Cannot create an instance of an abstract class. ``` Note what an abstract class still is: a real class. It can declare fields, a constructor, static members, and fully implemented methods; it can itself extend another class. The constructor is not decorative — a subclass invokes it through `super(...)`, and the field assignments in it run for every instance of every subclass. The only thing taken away is the ability to write `new Shape(...)` against the abstract type. ## `abstract` on a member An abstract member is a **signature without an implementation**: ```ts abstract class Repository { abstract find(id: string): Promise<string>; // method abstract tableName: string; // property abstract get isReady(): boolean; // accessor } ``` The rules the compiler enforces around these: - An abstract member may appear **only inside an abstract class** — put one in a normal class and you get *Abstract methods can only appear within an abstract class*. - It may not carry a body. Adding one gives *Method 'find' cannot have an implementation because it is marked abstract*. - It cannot be combined with `static`, and it cannot be combined with `private` — an abstract member has to be visible to the subclass that implements it, so `protected abstract` and public `abstract` are the two useful forms. - An abstract property declaration produces no field at runtime. It only records that instances of the type have a member of that shape. ## What a subclass owes A **non-abstract** subclass must implement every abstract member it inherits, all the way up the chain: ```ts class Circle extends Shape { constructor(private radius: number) { super('circle'); } area(): number { return Math.PI * this.radius ** 2; } } ``` Omit `area` and the compiler says *Non-abstract class 'Circle' does not implement inherited abstract member 'area' from class 'Shape'*. The message names each missing member, so a class inheriting three unimplemented members reports three errors. A subclass that is itself declared `abstract` may leave some or all of them unimplemented and pass the obligation down — a common shape in layered hierarchies where a middle class implements the boring half of the contract and defers the interesting half. ## Why the feature exists: the template method shape The payoff is that the base class can write an algorithm **in terms of members it does not have yet**: ```ts abstract class Importer { run(rows: string[]): number { let ok = 0; for (const row of rows) { if (this.validate(row)) { this.persist(row); ok++; } } return ok; } protected abstract validate(row: string): boolean; protected abstract persist(row: string): void; } ``` `run` is inherited and reused; `validate` and `persist` are holes the compiler forces each subclass to fill. This is what makes an abstract class different from a bare shape declaration: the shared implementation and the mandatory hook ship together in a single type. The general design tradeoff — when a base class with holes beats composition — is language-agnostic and belongs to object-oriented design; what TypeScript adds is the compile-time guarantee that no subclass slips through with a hole left open. ## Type versus value The abstract class name is usable in both positions, but they behave differently. As a **type**, `Shape` names the instance type including the abstract members, so `function draw(s: Shape) { s.area(); }` type-checks fine and accepts any concrete subclass instance. As a **value**, the class binding exists but its constructor is not callable with `new`. That split is why a variable annotated `Shape` is ordinary and useful, while `new Shape(...)` is the error above. ## It is a compile-time promise Everything here is checked by `tsc` and then erased. The emitted JavaScript is an ordinary class, and nothing in it re-checks abstractness at runtime — so the guarantee is only as strong as the type checking around the call site.
- Can an abstract class have a constructor, and if you can never call `new` on it, what runs that constructor?Yes, and it runs on every subclass instance. The subclass constructor calls `super(...)`, which executes the abstract class's constructor body — field assignments, argument validation, whatever it holds. `abstract` removes only the ability to construct the class directly; it does not make the constructor dead code.
- What happens if a subclass of an abstract class implements only some of the inherited abstract members?It fails to compile unless the subclass is itself declared `abstract`. A non-abstract class must implement every inherited abstract member, and the compiler reports each missing one by name. Marking the intermediate class `abstract` is the legitimate way to implement part of the contract and pass the rest down.
- Why does TypeScript reject `private abstract` on a member?A private member is invisible to subclasses, so no subclass could ever supply the implementation the modifier demands — the two modifiers ask for contradictory things and the compiler rejects the combination. When you want a hook that is internal to the hierarchy but still implementable, use `protected abstract`.
An abstract class is a form with some fields pre-filled and others left blank and marked required: you cannot submit the form as-is, and the office rejects any copy that comes back with a required blank still empty.
saying these in an interview costs you the question
- Thinks an abstract class cannot contain implemented methods or state
- Says the abstract class's constructor never runs
- Believes abstract members are optional for subclasses to implement
- Writes an abstract method with a body as a default implementation
- Assumes `abstract` blocks instantiation at runtime too