Classes & OOP
Organizing Code Into Objects
Open interactive version (quiz + challenge)Real-world analogy
A class is like a 3D printer mold. The mold defines the shape — every figure that comes out has the same structure (properties) and movable joints (methods). Objects are the printed figures. Change the mold, and every future figure changes. OOP is designing great molds!
What is it?
Classes bundle data (properties) and actions (methods) together. TypeScript adds access modifiers (public/private/protected) to control visibility. Inheritance lets classes extend parent classes. The 4 pillars of OOP are encapsulation, inheritance, polymorphism, and abstraction.
Real-world relevance
NestJS services are classes. React class components are classes. Every enterprise app uses classes to organize business logic!
Key points
- Classes = Blueprints — class User { name: string; constructor(n: string) { this.name = n; } }. Define ONCE, create MANY objects from it.
- Constructor — the Setup — constructor(name: string) { this.name = name; } runs automatically when you do new User('John'). It initializes the object.
- Properties & Methods — Properties store data. Methods are functions. A User has name (property) and greet() (method). Properties = nouns. Methods = verbs!
- Access Modifiers: public/private/protected — public (default) = anyone accesses. private = only THIS class. protected = this class + child classes. Use private to hide implementation details!
- Readonly — Can't Change — readonly id: string means you can READ it but never MODIFY it. Great for IDs and timestamps that should never change.
- Inheritance with extends — class Dog extends Animal { } means Dog is an Animal PLUS extra stuff. Dog gets ALL of Animal's properties and methods.
- Implements Interface — class Report implements Printable { } means Report promises to have everything Printable requires. TypeScript checks it!
- Abstract Classes — abstract class Shape { abstract area(): number; } cannot create Shape directly — MUST extend it. Forces child classes to implement abstract methods!
Code example
// Simple class
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hi, I'm ${this.name}`;
}
}
const john = new User("John", 25);
john.greet(); // "Hi, I'm John"
// Shorthand constructor (more common!)
class User {
constructor(
public name: string, // auto-creates this.name
public age: number, // auto-creates this.age
private password: string // auto-creates this.password (private!)
) {}
}
// Access modifiers
class User {
public name: string; // anyone can access (default)
private password: string; // only User class can access
protected role: string; // User + child classes can access
readonly id: string; // can READ, cannot CHANGE
constructor(name: string, password: string) {
this.id = "auto-generated";
this.name = name;
this.password = password;
this.role = "user";
}
private hashPassword(): string {
return "hashed_" + this.password;
}
}
const u = new User("John", "secret");
u.name; // OK — public
// u.password; // ERROR — private
// u.id = "x"; // ERROR — readonly
// Inheritance
class Animal {
constructor(public name: string) {}
speak(): string {
return `${this.name} makes a sound`;
}
}
class Dog extends Animal {
constructor(name: string, public breed: string) {
super(name); // call parent constructor
}
// Override parent method
speak(): string {
return `${this.name} barks!`;
}
}
const dog = new Dog("Rex", "Labrador");
dog.speak(); // "Rex barks!"
// Implements interface
interface Printable {
print(): void;
}
class Report implements Printable {
print(): void {
console.log("Printing...");
}
}
// Abstract class
abstract class Shape {
abstract area(): number; // child MUST implement
describe(): string { // shared method
return `Area is ${this.area()}`;
}
}
class Circle extends Shape {
constructor(private radius: number) {
super();
}
area(): number {
return Math.PI * this.radius ** 2;
}
}
// const s = new Shape(); // ERROR — cannot create abstract
const c = new Circle(5);
c.area(); // ~78.54Line-by-line walkthrough
- 1. Simple class
- 2. Declaring a class
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9. Closing block
- 10.
- 11.
- 12. Returning a value
- 13. Closing block
- 14. Closing block
- 15.
- 16. Declaring a variable
- 17.
- 18.
- 19. Shorthand constructor (more common!)
- 20. Declaring a class
- 21.
- 22.
- 23.
- 24.
- 25.
- 26. Closing block
- 27.
- 28. Access modifiers
- 29. Declaring a class
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40. Closing block
- 41.
- 42.
- 43. Returning a value
- 44. Closing block
- 45. Closing block
- 46.
- 47. Declaring a variable
- 48.
- 49. u.password; // ERROR — private
- 50. u.id = "x"; // ERROR — readonly
- 51.
- 52. Inheritance
- 53. Declaring a class
- 54.
- 55.
- 56.
- 57. Returning a value
- 58. Closing block
- 59. Closing block
- 60.
- 61. Declaring a class
- 62.
- 63.
- 64. Closing block
- 65.
- 66. Override parent method
- 67.
- 68. Returning a value
- 69. Closing block
- 70. Closing block
- 71.
- 72. Declaring a variable
- 73.
- 74.
- 75. Implements interface
- 76. Defining an interface shape
- 77.
- 78. Closing block
- 79.
- 80. Declaring a class
- 81.
- 82. Printing output to the console
- 83. Closing block
- 84. Closing block
- 85.
- 86. Abstract class
- 87.
- 88.
- 89.
- 90. Grouping related tests together
- 91. Returning a value
- 92. Closing block
- 93. Closing block
- 94.
- 95. Declaring a class
- 96.
- 97.
- 98. Closing block
- 99.
- 100.
- 101. Returning a value
- 102. Closing block
- 103. Closing block
- 104.
- 105. const s = new Shape(); // ERROR — cannot create abstract
- 106. Declaring a variable
- 107.
Spot the bug
class Dog {
private name: string;
constructor(name: string) {
this.name = name;
}
}
const dog = new Dog("Rex");
console.log(dog.name);Need a hint?
Check the access modifier on the name property...
Show answer
The property 'name' is private, so dog.name cannot be accessed outside the class. Fix: change 'private' to 'public', or add a public getName() method.
Explain like I'm 5
A class is like a cookie cutter. The cookie cutter (class) is the shape, and each cookie you make (object) looks the same but can have different sprinkles. You design the cutter once, then stamp out as many cookies as you want!
Fun fact
The 4 Pillars of OOP: Encapsulation (hiding details), Inheritance (extending classes), Polymorphism (same method, different behavior), Abstraction (simplifying complexity). Master these and you master OOP!
Hands-on challenge
Create a class hierarchy: Animal (with speak method), then Dog and Cat that override it. Create instances and call speak on each!
More resources
- MDN JavaScript Classes (MDN Web Docs)
- TypeScript Classes (TypeScript Official)
- Object Oriented Programming in JavaScript (Fireship)