In TypeScript an interface may extend a class, as in `interface Renderable extends Widget { label: string }`. What does the interface inherit from the class, and what restricts which types can implement it?
answer
- the instance side only
- no bodies, no constructor, no statics
- private members travel with it
- structural typing has one nominal escape
- only subclasses can implement it
basics
~20 sThe interface inherits the class's instance-side members — property and method types, without any implementations, and without the constructor or statics. It also inherits private and protected members, and because those are nominal, only the class itself or a subclass can implement the interface.
solid answer
~50 s`extends` on a class refers to the class's **instance type**: every property and method signature an instance has, with the bodies stripped. The constructor and any `static` members are not part of it. The interesting part is that `private` and `protected` members come along too, and TypeScript treats those nominally — a private field is identified with the exact declaration that introduced it, not with its name and type. So a freestanding object literal or an unrelated class can never satisfy the interface, because it has no way to produce that particular private member. Only `Widget` itself and its subclasses can. That makes the pattern a deliberate way to say "implementers must descend from this class" in an otherwise structural type system. If the class has no private or protected members, the restriction disappears and any structurally matching value works.
code
typescript · 16 linesclass Widget {
private token = "internal";
render(): string { return "widget"; }
}
// The interface picks up token and render, plus its own member.
interface Renderable extends Widget {
label: string;
}
// Only Widget or a subclass can supply the nominal private member.
class Button extends Widget implements Renderable {
label = "OK";
}
const b: Renderable = new Button();go deeper
Know that an interface may extend a class and picks up the shapes of its instance members — the property and method types, never the code inside them.
Explain that the inherited set is the instance type without the constructor or statics, and that inherited private or protected members are matched nominally, so only subclasses can implement the result.
Show when the sealing effect is worth the coupling — a plugin contract that must share base-class internals — and flag that adding a private member to such a base is a breaking change for structural implementers.
Weigh nominal sealing against extensibility in a public API: it forecloses independent implementations and test doubles, so reserve it for contracts whose invariants genuinely cannot be established outside the hierarchy.
## The instance side, without implementations A class declaration in TypeScript introduces two things: a value (the constructor function) and a type (the type of its instances). When an interface extends a class, it takes the **instance type** — the properties and method signatures, minus the bodies. ```typescript class Widget { id = "w"; render(): string { return "widget"; } static create(): Widget { return new Widget(); } } interface Renderable extends Widget { label: string; } // Renderable requires: id: string, render(): string, label: string // It does NOT include the constructor or the static create(). ``` Implementations are not inherited — an interface never carries behaviour, so anything implementing `Renderable` must supply its own `render`. ## Private and protected members are nominal TypeScript's assignability is normally structural: two types with the same members are interchangeable regardless of where they were declared. `private` and `protected` members are the deliberate exception. A private member is identified by the declaration that introduced it, so two classes each declaring `private token: string` are *not* mutually assignable. That rule is what gives interface-extends-class its distinctive behaviour: ```typescript class Widget { private token = "internal"; render(): string { return "widget"; } } interface Renderable extends Widget { label: string; } class Button extends Widget implements Renderable { label = "OK"; // inherits token and render from Widget — OK } // class Rogue implements Renderable { // Error: property 'token' is missing, // label = "no"; // and it cannot be supplied from outside // render() { return "rogue"; } // because Widget's private token is nominal. // } ``` Even if `Rogue` wrote its own `private token = "internal"`, it would still be rejected: the member would be a different declaration. The only way to obtain `Widget`'s private member is to inherit it, which means extending `Widget`. ## Why you would want this The pattern is a controlled sealing mechanism in a structurally typed language. It says: this contract may only be satisfied by types inside my inheritance hierarchy. Typical uses are a plugin or visitor contract where implementers must share the base class's internal machinery, and library surfaces that want to prevent third parties from hand-rolling a look-alike whose invariants have not been established by the base constructor. If the class has no `private` or `protected` members, nothing is sealed and the interface behaves like any other structural shape — an object literal with matching members satisfies it. ## Distinguishing it from the neighbouring syntax Three clauses use similar words and mean different things: - `class B extends A` — inheritance of implementation and type. - `class B implements I` — a check that B satisfies I; nothing is inherited. - `interface I extends A` — I's member list gains A's instance members; no implementations, no statics, no constructor. Only the first produces any JavaScript. An interface, whatever it extends, is erased entirely at compile time; the `private` restriction is a compile-time check with no runtime counterpart whatsoever. A cast can defeat it, exactly as a cast defeats every other type-level guarantee. ## Practical notes - Prefer extending an interface, not a class, unless you specifically want the sealing effect — coupling a contract to a concrete class ties every implementer's compile-time surface to that class's evolution. - Adding a private member to a widely used base class is a breaking change for anyone who was satisfying a derived interface structurally. - The instance type is what you get. If you need the constructor or statics, you are asking a different question about the class's static side.
- Does the interface also pick up the class's constructor or its static members?No. `extends` on a class refers to the instance type only, so statics and the constructor signature are excluded. That is deliberate: an interface describes what instances look like, and a class's static side is a separate type reachable through `typeof TheClass`.
- What happens if the class has no private or protected members at all?Nothing is sealed. The inherited members are all public, so assignability is purely structural again and any object literal or unrelated class with matching members satisfies the interface. The restriction is a side effect of the nominal treatment of private and protected, not of extending a class as such.
- Can the restriction be defeated?At compile time only through an assertion — `x as Renderable` bypasses it as it bypasses every other check. There is no runtime enforcement at all: interfaces are erased entirely, and the private modifier is a compile-time rule, so nothing in the emitted JavaScript records that a value came from the right hierarchy.
saying these in an interview costs you the question
- Says the interface inherits the class's method implementations
- Thinks static members and the constructor come along too
- Claims any class redeclaring the same private member satisfies it
- Believes the private restriction is enforced at runtime
- Confuses it with a class implementing an interface