TypeScript Fundamentals
Course 1 · Chapter 5 · Classes & Object-Oriented TypeScript
🏗️ Classes & Object-Oriented TypeScript
Classes are the core of object-oriented programming. They let you create blueprints for objects, define behavior with methods, enforce constraints with access modifiers, and share functionality through inheritance. TypeScript adds type safety to JavaScript's class system.
🏛️ Basic Class Structure
Your First Class
class Dog {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
bark(): void {
console.log(`${this.name} says woof!`);
}
}
const myDog = new Dog("Buddy", 3);
myDog.bark();
Key parts:
class Dog { } — defines the blueprint
name: string; age: number; — properties (with types!)
constructor() — runs when you create a new instance
bark() — a method (function inside the class)
this — refers to the current instance
new Dog() — creates an instance
🔐 Access Modifiers
Access modifiers control who can access properties and methods. TypeScript provides three: public, private, and protected.
public
Default. Anyone can access from anywhere.
class User {
public name: string;
}
const user = new User();
user.name = "Alice";
private
Only accessible inside the class.
class User {
private password: string;
}
const user = new User();
console.log(user.password);
protected
Accessible inside the class and subclasses.
class Animal {
protected health: number;
}
class Dog extends Animal {
heal() { this.health++; }
}
readonly
Can't be changed after initialization.
class User {
readonly id: number;
constructor(id: number) {
this.id = id;
}
}
const user = new User(1);
user.id = 2;
Why Access Modifiers Matter
Consider a bank account:
class BankAccount {
private balance: number = 0;
deposit(amount: number): void {
if (amount > 0) this.balance += amount;
}
withdraw(amount: number): boolean {
if (amount <= this.balance) {
this.balance -= amount;
return true;
}
return false;
}
public getBalance(): number {
return this.balance;
}
}
const account = new BankAccount();
account.deposit(100);
console.log(account.getBalance());
account.balance = 1000000;
✅ Private balance enforces business logic:
You can't directly set balance to an invalid amount. You must use deposit/withdraw, which validate the transaction.
👨👩👧👦 Inheritance
Inheritance lets you create a class that extends another, inheriting its properties and methods while adding its own.
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(): void {
console.log(`${this.name} is moving`);
}
}
class Dog extends Animal {
bark(): void {
console.log(`${this.name} says woof!`);
}
move(): void {
console.log(`${this.name} runs fast`);
}
}
const dog = new Dog("Buddy");
dog.move();
dog.bark();
super: Calling Parent Methods
Use super to call parent class methods:
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name);
this.breed = breed;
}
move(): void {
super.move();
console.log(` (as a ${this.breed})`);
}
}
🎭 Abstract Classes
An abstract class is a blueprint that can't be instantiated directly. Its purpose is to define methods that subclasses must implement. Think of it as a contract.
abstract class Shape {
abstract getArea(): number;
public describe(): void {
console.log(`This shape has area: ${this.getArea()}`);
}
}
const shape = new Shape();
class Circle extends Shape {
radius: number;
constructor(radius: number) {
super();
this.radius = radius;
}
getArea(): number {
return Math.PI * this.radius ** 2;
}
}
const circle = new Circle(5);
circle.describe();
⚡ Constructor Parameter Shorthand
TypeScript lets you declare and initialize properties directly in the constructor:
class User {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
class User {
constructor(
public name: string,
public age: number
) {}
}
💻 Coding Challenges
Challenge 1: Create a Vehicle Class
Create a Vehicle class with:
- Properties: brand, model, year (all public)
- Constructor to initialize these
- A method
getInfo() that returns a description string
Goal: Create 2-3 vehicle instances and call getInfo().
→ Solution
Challenge 2: Access Modifiers
Create a BankAccount class with:
- Private
balance property (starts at 0)
- Public
deposit(amount) method
- Public
getBalance() method
- Ensure balance can't go negative
Goal: Demonstrate why private properties protect data.
→ Solution
Challenge 3: Inheritance and Overriding
Create an Animal class with a speak() method. Then create Dog and Cat subclasses that override speak() with their own sounds.
- Animal: speaks generically
- Dog: says "Woof!"
- Cat: says "Meow!"
Goal: Demonstrate polymorphism and method overriding.
→ Solution
💡 Real-World Tip: Use private by Default
Start by making properties private, then expose what you need through public methods. This is safer than making everything public and trying to restrict later. It forces you to think about your API and protects internal state from accidental changes.
🎯 What's Next
Classes are powerful, but sometimes you need more flexibility. Chapter 6 introduces Generics — the TypeScript way to write code that works with many types while staying type-safe.