Classes in TypeScript
A TypeScript class is an ES2015 class plus a layer of types and access control on top of it. All runtime behavior (fields, the constructor, methods, static, prototype-based inheritance) is plain JavaScript. Everything TypeScript adds — field types, the public/private/protected modifiers, readonly, abstract — lives only at compile time and disappears from the emitted code. Understanding that boundary — what is real at runtime versus what is merely a compiler check — is the dividing line of this whole topic.
That leads straight to the classic beginner trap: treating private and readonly as runtime guarantees. They are not. A private field is freely readable from the compiled JavaScript, and a readonly array still accepts push. Genuine runtime privacy comes only from an ECMAScript #field. Below are seven layers, from the basic class members to abstract classes.
Topic map
- Classes and Members — typed fields, the
constructor, methods,staticmembers; what TypeScript adds over ES2015 classes. - Access Modifiers —
public/private/protected, why they are erased at runtime, and why you need#field. - readonly Fields — assign-once, compile-time only, pins the binding rather than the contents.
- Accessors (get/set) — intercepting property reads and writes, computed properties, a getter with no setter.
- Parameter Properties — constructor shorthand, what actually creates the field,
constructor-only. - Inheritance —
extends,super()beforethis,override, single inheritance only. - Abstract Classes — a non-instantiable blueprint carrying implementation and state,
abstractmethods, the difference from an interface.
Common Mistakes and Traps
| Mistake | Consequence |
|---|---|
Treating private/readonly as a runtime guarantee | Both are erased at compile time; the field is readable and mutable from JavaScript |
Conflating private with an ECMAScript #field | Only #field is truly private at runtime; private is a compile-time discipline |
Accessing a static member through an instance | It is not there — static lives on the class, not on instances |
Expecting a subclass to get its own copy of a static field | static is one slot for the whole hierarchy; Child.count++ also changes Base.count |
Swapping private and protected | Subclass access belongs to protected, not private |
Expecting a readonly array to be immutable | Only the binding is pinned; push still works — the contents need readonly T[] |
| Calling an accessor with parentheses | obj.name, not obj.name() — otherwise you get the function, not the value |
| Expecting a getter with no setter to give runtime immutability | It is read-only at compile time; at runtime the descriptor can be redefined |
| Thinking a plain parameter becomes a field | Only a modifier or readonly before the parameter creates a field |
Duplicating this.x = x next to a parameter property | The assignment is already generated — the manual copy is redundant |
Expecting parameter properties outside the constructor | The mechanism works only in the constructor, not in ordinary methods |
Using this before super(...) | A compile-time and runtime error in a subclass constructor |
Overriding a method without override under noImplicitOverride | A typo in the name creates a new method instead of an override — the flag catches it |
Expecting multiple inheritance (extends A, B) | Single inheritance only; multiple contracts go through implements |
Trying to new an abstract class | An abstract class cannot be instantiated directly |
Leaving an abstract method unimplemented in a subclass | The subclass must itself become abstract, otherwise it is a compile error |
Interview relevance
TypeScript classes come up almost every time — and the check is not syntax but your grasp of the boundary between the type system and the runtime. A candidate who says "private is a compiler check, it is erased, and real privacy is #field" immediately stands out from "private hides the field".
Typical checks:
- Which members a class offers — typed fields, methods,
static,readonly, accessors — and which of these TypeScript adds over ES2015. - The difference between
public/private/protected, that they only work at compile time, and how#fielddiffers fromprivate. - That a
staticfield is one shared slot for the whole hierarchy, not a copy per subclass. - That
readonlypins the binding, not the contents, and that it too is erased. - How
get/setintercept access and when a getter makes a property read-only. - That a parameter property is created by the modifier, not by the parameter itself, and only in the
constructor. - The
super()-before-thisrule, the role ofoverrideundernoImplicitOverride, and why inheritance is single-only. - How an abstract class differs from an interface — implementation, state, and presence at runtime.
Common wrong answer: "private physically hides the field." That opens the discussion that access modifiers are a compile-time discipline tool, not runtime protection, and that #field exists for genuine privacy. The second common failure is treating a readonly array as immutable: the binding is pinned, but push still goes through, because readonly protects the slot, not what sits in it.