
Quick answer: TypeScript classes add visibility modifiers, typed fields and parameter properties to plain JavaScript classes. Use private and readonly to express intent, implements to guarantee a shape, and abstract for a base class that is never instantiated itself. Prefer composition over deep inheritance chains.The sections below follow a class from its typed fields through parameter properties, inheritance and interfaces, and end with composition, which is usually the better answer when TypeScript classes start to grow.
Table of Contents
Your first typed TypeScript class
class Product {
readonly id: number;
name: string;
private stock = 0;
constructor(id: number, name: string) {
this.id = id;
this.name = name;
}
restock(units: number): void {
if (units <= 0) throw new RangeError("units must be positive");
this.stock += units;
}
get available(): boolean {
return this.stock > 0;
}
}readonly— assignable only in the constructor, soidcannot change later.private— visible only inside the class; this is a compile-time check, not a runtime guarantee.get— a computed property read like a field, so callers writeproduct.available.
Parameter properties: less boilerplate
When a constructor parameter is marked with a modifier, TypeScript declares and assigns the field for you. The class above shortens to this:
class Product {
private stock = 0;
constructor(
readonly id: number,
public name: string,
) {}
restock(units: number): void {
this.stock += units;
}
}Inheritance and abstract classes
abstract class Shape {
constructor(public readonly name: string) {}
abstract area(): number; // subclasses must implement this
describe(): string {
return `${this.name} has area ${this.area()}`;
}
}
class Circle extends Shape {
constructor(public radius: number) {
super("circle");
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
// new Shape("x"); // error — abstract classes cannot be instantiatedCall super() before touching this in a subclass constructor, and mark overridden methods with override so a typo in the parent method name becomes an error rather than a new method.
Interfaces versus TypeScript classes
An interface describes a shape with no implementation; a class provides implementation. implements makes a class promise to satisfy an interface, which is useful when several classes should be interchangeable.
interface Describable {
describe(): string;
}
class Tag implements Describable {
constructor(private label: string) {}
describe(): string {
return `#${this.label}`;
}
}
function printAll(items: Describable[]) {
items.forEach(i => console.log(i.describe()));
}Because printAll depends only on Describable, not on a concrete class, you can pass anything that fits — a real class, a plain object literal, or a test double. That is the practical payoff of programming to interfaces.
Composition beats deep inheritance
Inheritance is good for a genuine is-a relationship — a Circle is a Shape. It becomes fragile when chains grow three or four levels deep, because a change at the top ripples everywhere. For has-a relationships, compose instead.
class Logger {
log(msg: string) { console.log(`[${new Date().toISOString()}] ${msg}`); }
}
class UserService {
private logger = new Logger(); // has a logger, does not extend one
create(name: string) {
this.logger.log(`Created ${name}`);
}
}Next: generics, where types become parameters. To learn TypeScript properly with a mentor, Ampersand Academy teaches web development one-to-one.
Frequently asked questions
Are TypeScript private fields actually private at runtime?
No. The private modifier is a compile-time check only. For real runtime privacy, use a JavaScript hash-private field written with a leading hash, which the engine enforces.
What is an abstract class in TypeScript?
A class that cannot be instantiated directly and may declare abstract members that subclasses must implement. It is the base for a family of related classes.
What is the difference between an interface and a class?
An interface describes a shape with no implementation, while a class provides the implementation. A class can implement an interface to guarantee it satisfies that shape.
What are parameter properties in TypeScript?
A shorthand where adding public, private, protected or readonly to a constructor parameter declares and assigns a field automatically, removing the repeated this.x equals x lines.
Should I use inheritance or composition?
Use inheritance for a genuine is-a relationship such as Circle is a Shape. Use composition for has-a relationships, because deep inheritance chains are fragile to change.
Last updated on · Written by Dinesh Kumar R

