Prerequisites
- Basic TypeScript types and interfaces ๐
- Understanding of functions and objects ๐ง
- Familiarity with ES6 syntax ๐ก
What you'll learn
- Create and instantiate TypeScript classes ๐ฏ
- Understand constructors and class methods ๐ ๏ธ
- Apply access modifiers for encapsulation ๐
- Build real-world applications with classes ๐๏ธ
๐ฏ Introduction
Welcome to the exciting world of Object-Oriented Programming with TypeScript classes! ๐ Think of classes as blueprints for creating objects - like a cookie cutter that shapes delicious cookies! ๐ช
Classes help you organize your code into reusable, structured components that model real-world entities. Whether youโre building a game ๐ฎ, an e-commerce app ๐, or a social media platform ๐ฑ, classes are your best friend for creating maintainable code.
By the end of this tutorial, youโll be confidently creating classes that make your fellow developers say โWow, this code is so clean and organized!โ ๐
Letโs dive into the wonderful world of TypeScript classes! ๐โโ๏ธ
๐ Understanding Classes
๐ค What are Classes?
Classes are like templates or blueprints for creating objects ๐๏ธ. Think of them as:
- ๐ช Cookie cutters: The class is the cutter, objects are the cookies
- ๐ House blueprints: The class is the blueprint, instances are actual houses
- ๐ Car factory: The class defines how to make cars, instances are individual cars
In TypeScript, classes combine:
- ๐ Data (properties) - what the object has
- โก Behavior (methods) - what the object can do
- ๐ Encapsulation - controlling access to data
๐ก Why Use Classes?
Hereโs why developers love classes:
- Code Organization ๐: Group related data and functions together
- Reusability โป๏ธ: Create multiple instances from one blueprint
- Encapsulation ๐: Hide internal details, expose clean interfaces
- Inheritance ๐ฅ: Share common functionality between related classes
- Type Safety ๐ก๏ธ: TypeScript ensures your classes are used correctly
Real-world example: A User
class can represent users in your app, with properties like name
and email
, and methods like login()
and updateProfile()
๐ค.
๐ง Basic Class Syntax
๐ Your First Class
Letโs create a simple class to understand the basics:
// ๐ฎ A simple Player class for a game
class Player {
// ๐ Properties (what the player has)
name: string;
level: number;
health: number;
// ๐๏ธ Constructor (how to create a player)
constructor(name: string) {
this.name = name;
this.level = 1; // ๐ Starting level
this.health = 100; // โค๏ธ Full health to start
}
// โก Methods (what the player can do)
levelUp(): void {
this.level++;
this.health = 100; // ๐ฉน Full heal on level up!
console.log(`๐ ${this.name} reached level ${this.level}!`);
}
takeDamage(damage: number): void {
this.health -= damage;
console.log(`๐ฅ ${this.name} took ${damage} damage! Health: ${this.health}`);
if (this.health <= 0) {
console.log(`๐ ${this.name} has been defeated!`);
}
}
// ๐ Getter method
getStatus(): string {
return `๐ค ${this.name} - Level ${this.level} (${this.health}โค๏ธ)`;
}
}
// ๐ Creating instances (actual players)
const hero = new Player("Aragorn");
const mage = new Player("Gandalf");
console.log(hero.getStatus()); // ๐ค Aragorn - Level 1 (100โค๏ธ)
hero.levelUp(); // ๐ Aragorn reached level 2!
hero.takeDamage(30); // ๐ฅ Aragorn took 30 damage! Health: 70
๐ก Key Concepts:
class
keyword defines the blueprintconstructor()
runs when creating new instancesthis
refers to the current instance- Properties store data, methods define behavior
๐ก Practical Examples
๐ Example 1: Shopping Cart System
Letโs build a real-world shopping cart:
// ๐๏ธ Product interface for type safety
interface Product {
id: string;
name: string;
price: number;
emoji: string;
}
// ๐ Cart item with quantity
interface CartItem {
product: Product;
quantity: number;
}
// ๐ Shopping Cart class
class ShoppingCart {
// ๐ Private properties (internal data)
private items: CartItem[] = [];
private customerName: string;
// ๐๏ธ Constructor
constructor(customerName: string) {
this.customerName = customerName;
console.log(`๐ New cart created for ${customerName}!`);
}
// โ Add item to cart
addItem(product: Product, quantity: number = 1): void {
// ๐ Check if item already exists
const existingItem = this.items.find(item => item.product.id === product.id);
if (existingItem) {
existingItem.quantity += quantity;
console.log(`๐ Updated ${product.emoji} ${product.name} quantity to ${existingItem.quantity}`);
} else {
this.items.push({ product, quantity });
console.log(`โ
Added ${quantity}x ${product.emoji} ${product.name} to cart`);
}
}
// โ Remove item from cart
removeItem(productId: string): void {
const index = this.items.findIndex(item => item.product.id === productId);
if (index !== -1) {
const removedItem = this.items.splice(index, 1)[0];
console.log(`๐๏ธ Removed ${removedItem.product.emoji} ${removedItem.product.name} from cart`);
} else {
console.log(`โ Product not found in cart`);
}
}
// ๐ฐ Calculate total price
getTotal(): number {
return this.items.reduce((total, item) => {
return total + (item.product.price * item.quantity);
}, 0);
}
// ๐ Display cart contents
displayCart(): void {
console.log(`\n๐ ${this.customerName}'s Cart:`);
console.log('='.repeat(30));
if (this.items.length === 0) {
console.log('๐ญ Your cart is empty');
return;
}
this.items.forEach(item => {
const total = item.product.price * item.quantity;
console.log(`${item.product.emoji} ${item.product.name} x${item.quantity} - $${total.toFixed(2)}`);
});
console.log('โ'.repeat(30));
console.log(`๐ฐ Total: $${this.getTotal().toFixed(2)}`);
}
// ๐งน Clear cart
clearCart(): void {
this.items = [];
console.log(`๐งน Cart cleared for ${this.customerName}`);
}
// ๐ Checkout process
checkout(): void {
if (this.items.length === 0) {
console.log('โ Cannot checkout empty cart');
return;
}
console.log(`\n๐ Processing checkout for ${this.customerName}...`);
this.displayCart();
console.log(`โ
Order placed successfully! Thank you, ${this.customerName}! ๐`);
this.clearCart();
}
}
// ๐๏ธ Sample products
const products: Product[] = [
{ id: '1', name: 'TypeScript Book', price: 29.99, emoji: '๐' },
{ id: '2', name: 'Mechanical Keyboard', price: 149.99, emoji: 'โจ๏ธ' },
{ id: '3', name: 'Coffee Mug', price: 12.99, emoji: 'โ' },
{ id: '4', name: 'Stress Ball', price: 7.99, emoji: '๐ก' }
];
// ๐ Let's go shopping!
const cart = new ShoppingCart("Alice");
cart.addItem(products[0], 2); // 2 books
cart.addItem(products[1]); // 1 keyboard
cart.addItem(products[2], 3); // 3 mugs
cart.displayCart();
cart.removeItem('2'); // Remove keyboard
cart.displayCart();
cart.checkout();
๐ต Example 2: Music Player Class
Letโs create a feature-rich music player:
// ๐ต Song interface
interface Song {
id: string;
title: string;
artist: string;
duration: number; // in seconds
genre: string;
emoji: string;
}
// ๐ถ Music Player class
class MusicPlayer {
private playlist: Song[] = [];
private currentSongIndex: number = -1;
private isPlaying: boolean = false;
private volume: number = 50; // 0-100
private shuffleMode: boolean = false;
private repeatMode: 'none' | 'one' | 'all' = 'none';
// ๐ Player stats
private totalPlayTime: number = 0;
constructor() {
console.log('๐ต Music Player initialized! Ready to rock! ๐ธ');
}
// โ Add song to playlist
addSong(song: Song): void {
this.playlist.push(song);
console.log(`โ
Added ${song.emoji} "${song.title}" by ${song.artist}`);
}
// โถ๏ธ Play current song
play(): void {
if (this.playlist.length === 0) {
console.log('โ No songs in playlist! Add some music first ๐ต');
return;
}
if (this.currentSongIndex === -1) {
this.currentSongIndex = 0;
}
this.isPlaying = true;
const song = this.playlist[this.currentSongIndex];
console.log(`โถ๏ธ Now playing: ${song.emoji} "${song.title}" by ${song.artist}`);
this.displayProgress();
}
// โธ๏ธ Pause playback
pause(): void {
if (this.isPlaying) {
this.isPlaying = false;
console.log('โธ๏ธ Playback paused');
} else {
console.log('โ Music is not playing');
}
}
// โน๏ธ Stop playback
stop(): void {
this.isPlaying = false;
console.log('โน๏ธ Playback stopped');
}
// โญ๏ธ Next song
next(): void {
if (this.playlist.length === 0) return;
if (this.shuffleMode) {
this.currentSongIndex = Math.floor(Math.random() * this.playlist.length);
} else {
this.currentSongIndex = (this.currentSongIndex + 1) % this.playlist.length;
}
if (this.isPlaying) {
this.play();
}
}
// โฎ๏ธ Previous song
previous(): void {
if (this.playlist.length === 0) return;
this.currentSongIndex = this.currentSongIndex <= 0
? this.playlist.length - 1
: this.currentSongIndex - 1;
if (this.isPlaying) {
this.play();
}
}
// ๐ Toggle shuffle
toggleShuffle(): void {
this.shuffleMode = !this.shuffleMode;
const status = this.shuffleMode ? 'ON' : 'OFF';
console.log(`๐ Shuffle mode: ${status}`);
}
// ๐ Cycle repeat mode
cycleRepeat(): void {
const modes: ('none' | 'one' | 'all')[] = ['none', 'one', 'all'];
const currentIndex = modes.indexOf(this.repeatMode);
const nextIndex = (currentIndex + 1) % modes.length;
this.repeatMode = modes[nextIndex];
const emojis = { none: 'โก๏ธ', one: '๐', all: '๐' };
console.log(`${emojis[this.repeatMode]} Repeat mode: ${this.repeatMode}`);
}
// ๐ Set volume
setVolume(level: number): void {
this.volume = Math.max(0, Math.min(100, level));
const volumeBar = 'โ'.repeat(Math.floor(this.volume / 10));
console.log(`๐ Volume: ${this.volume}% [${volumeBar}]`);
}
// ๐ Display current status
displayStatus(): void {
console.log('\n๐ต Music Player Status');
console.log('='.repeat(25));
if (this.currentSongIndex >= 0) {
const song = this.playlist[this.currentSongIndex];
const status = this.isPlaying ? 'โถ๏ธ Playing' : 'โธ๏ธ Paused';
console.log(`${status}: ${song.emoji} "${song.title}"`);
console.log(`๐ค Artist: ${song.artist}`);
console.log(`๐ญ Genre: ${song.genre}`);
}
console.log(`๐ Playlist: ${this.playlist.length} songs`);
console.log(`๐ Volume: ${this.volume}%`);
console.log(`๐ Shuffle: ${this.shuffleMode ? 'ON' : 'OFF'}`);
console.log(`๐ Repeat: ${this.repeatMode}`);
}
// ๐ Show playlist
showPlaylist(): void {
console.log('\n๐ Current Playlist:');
console.log('='.repeat(30));
this.playlist.forEach((song, index) => {
const current = index === this.currentSongIndex ? 'โถ๏ธ ' : ' ';
const duration = `${Math.floor(song.duration / 60)}:${(song.duration % 60).toString().padStart(2, '0')}`;
console.log(`${current}${song.emoji} "${song.title}" - ${song.artist} (${duration})`);
});
}
// ๐ Private helper method
private displayProgress(): void {
console.log('๐ต โโโโโโโโโโโโโโโโโโโโ 100%');
}
}
// ๐ต Sample songs
const songs: Song[] = [
{ id: '1', title: 'Bohemian Rhapsody', artist: 'Queen', duration: 355, genre: 'Rock', emoji: '๐' },
{ id: '2', title: 'Billie Jean', artist: 'Michael Jackson', duration: 294, genre: 'Pop', emoji: '๐บ' },
{ id: '3', title: 'Hotel California', artist: 'Eagles', duration: 391, genre: 'Rock', emoji: '๐จ' },
{ id: '4', title: 'Shape of You', artist: 'Ed Sheeran', duration: 234, genre: 'Pop', emoji: '๐' }
];
// ๐ Let's play some music!
const player = new MusicPlayer();
// Add songs to playlist
songs.forEach(song => player.addSong(song));
// Control playback
player.showPlaylist();
player.play();
player.setVolume(75);
player.toggleShuffle();
player.next();
player.displayStatus();
๐ Advanced Class Features
๐ Access Modifiers
Control who can access your class members:
class BankAccount {
public accountHolder: string; // โ
Accessible everywhere
private balance: number; // ๐ Only within this class
protected accountNumber: string; // ๐ฅ This class and subclasses
constructor(holder: string, initialBalance: number) {
this.accountHolder = holder;
this.balance = initialBalance;
this.accountNumber = this.generateAccountNumber();
}
// ๐ Private method - internal use only
private generateAccountNumber(): string {
return 'ACC-' + Math.random().toString(36).substr(2, 9).toUpperCase();
}
// โ
Public method - safe interface
public deposit(amount: number): void {
if (amount > 0) {
this.balance += amount;
console.log(`๐ฐ Deposited $${amount}. New balance: $${this.balance}`);
}
}
// โ
Public method with validation
public withdraw(amount: number): boolean {
if (amount > 0 && amount <= this.balance) {
this.balance -= amount;
console.log(`๐ธ Withdrew $${amount}. Remaining balance: $${this.balance}`);
return true;
} else {
console.log('โ Insufficient funds or invalid amount');
return false;
}
}
// โ
Safe way to check balance
public getBalance(): number {
return this.balance;
}
}
const account = new BankAccount("John Doe", 1000);
account.deposit(500); // โ
Works
console.log(account.getBalance()); // โ
Works
// console.log(account.balance); // โ Error: private property
๐๏ธ Static Members
Class-level properties and methods:
class MathUtils {
// ๐ Static properties - shared by all instances
static readonly PI = 3.14159;
static readonly E = 2.71828;
// ๐ง Static methods - utility functions
static add(a: number, b: number): number {
return a + b;
}
static multiply(a: number, b: number): number {
return a * b;
}
static circleArea(radius: number): number {
return MathUtils.PI * radius * radius;
}
// ๐ Statistics helper
static average(numbers: number[]): number {
if (numbers.length === 0) return 0;
const sum = numbers.reduce((total, num) => total + num, 0);
return sum / numbers.length;
}
}
// ๐ Use static methods without creating instances
console.log(`๐ Circle area: ${MathUtils.circleArea(5)}`);
console.log(`๐ Average: ${MathUtils.average([1, 2, 3, 4, 5])}`);
console.log(`๐ฅง Pi value: ${MathUtils.PI}`);
โ ๏ธ Common Pitfalls and Solutions
๐ฑ Pitfall 1: Forgetting โthisโ keyword
class Counter {
private count: number = 0;
// โ Wrong - missing 'this'
incrementWrong(): void {
count++; // โ Error: 'count' is not defined
}
// โ
Correct - using 'this'
increment(): void {
this.count++; // โ
Works!
console.log(`Count: ${this.count}`);
}
}
๐คฏ Pitfall 2: Not initializing properties
class Student {
name: string; // โ Not initialized
age: number; // โ Not initialized
grades: number[] = []; // โ
Initialized
constructor(name: string, age: number) {
this.name = name; // โ
Initialize in constructor
this.age = age; // โ
Initialize in constructor
}
}
๐ Pitfall 3: Exposing internal state
// โ Bad - exposes internal array
class BadTodoList {
public items: string[] = [];
}
// โ
Good - controls access
class GoodTodoList {
private items: string[] = [];
addItem(item: string): void {
this.items.push(item);
}
getItems(): readonly string[] {
return [...this.items]; // Return copy
}
}
๐ ๏ธ Best Practices
- ๐ฏ Single Responsibility: Each class should have one clear purpose
- ๐ Encapsulation: Keep internal details private
- ๐ Clear Naming: Use descriptive class and method names
- ๐๏ธ Constructor Logic: Keep constructors simple and focused
- โก Method Size: Keep methods small and focused
- ๐ Immutability: Consider readonly properties where appropriate
๐งช Hands-On Exercise
๐ฏ Challenge: Build a Library Management System
Create a comprehensive library system using classes:
๐ Requirements:
- ๐ Book class with title, author, ISBN, and availability
- ๐ค Member class with name, ID, and borrowed books
- ๐๏ธ Library class to manage books and members
- ๐ Track borrowing history and due dates
- ๐ Search functionality for books and members
๐ Bonus Points:
- Add late fee calculation
- Implement reservation system
- Create different member types (student, adult, premium)
๐ก Solution
๐ Click to see solution
// ๐ Book class
class Book {
readonly isbn: string;
readonly title: string;
readonly author: string;
readonly genre: string;
private isAvailable: boolean = true;
private borrowedBy: string | null = null;
private dueDate: Date | null = null;
constructor(isbn: string, title: string, author: string, genre: string) {
this.isbn = isbn;
this.title = title;
this.author = author;
this.genre = genre;
}
// ๐ Get book status
getStatus(): 'available' | 'borrowed' {
return this.isAvailable ? 'available' : 'borrowed';
}
// ๐
Get due date if borrowed
getDueDate(): Date | null {
return this.dueDate;
}
// ๐ค Get who borrowed it
getBorrower(): string | null {
return this.borrowedBy;
}
// ๐ Borrow book
borrow(memberId: string, dueDate: Date): boolean {
if (this.isAvailable) {
this.isAvailable = false;
this.borrowedBy = memberId;
this.dueDate = dueDate;
return true;
}
return false;
}
// ๐ Return book
return(): void {
this.isAvailable = true;
this.borrowedBy = null;
this.dueDate = null;
}
// ๐ Book info
getInfo(): string {
const status = this.isAvailable ? 'โ
Available' : 'โ Borrowed';
return `๐ "${this.title}" by ${this.author} (${this.genre}) - ${status}`;
}
}
// ๐ค Library Member class
class LibraryMember {
readonly memberId: string;
readonly name: string;
readonly email: string;
private borrowedBooks: string[] = []; // ISBNs
private joinDate: Date;
constructor(memberId: string, name: string, email: string) {
this.memberId = memberId;
this.name = name;
this.email = email;
this.joinDate = new Date();
}
// ๐ Get borrowed books
getBorrowedBooks(): readonly string[] {
return [...this.borrowedBooks];
}
// ๐ Borrow a book
borrowBook(isbn: string): void {
this.borrowedBooks.push(isbn);
}
// ๐ Return a book
returnBook(isbn: string): void {
const index = this.borrowedBooks.indexOf(isbn);
if (index !== -1) {
this.borrowedBooks.splice(index, 1);
}
}
// ๐ Member info
getInfo(): string {
return `๐ค ${this.name} (ID: ${this.memberId}) - ${this.borrowedBooks.length} books borrowed`;
}
}
// ๐๏ธ Library Management System
class Library {
private books: Map<string, Book> = new Map();
private members: Map<string, LibraryMember> = new Map();
private borrowingPeriodDays: number = 14;
// ๐ Add book to library
addBook(book: Book): void {
this.books.set(book.isbn, book);
console.log(`โ
Added book: ${book.getInfo()}`);
}
// ๐ค Register new member
registerMember(member: LibraryMember): void {
this.members.set(member.memberId, member);
console.log(`โ
Registered member: ${member.getInfo()}`);
}
// ๐ Borrow book
borrowBook(memberId: string, isbn: string): boolean {
const member = this.members.get(memberId);
const book = this.books.get(isbn);
if (!member) {
console.log(`โ Member ${memberId} not found`);
return false;
}
if (!book) {
console.log(`โ Book ${isbn} not found`);
return false;
}
if (book.getStatus() === 'borrowed') {
console.log(`โ Book "${book.title}" is already borrowed`);
return false;
}
// Calculate due date
const dueDate = new Date();
dueDate.setDate(dueDate.getDate() + this.borrowingPeriodDays);
// Process borrowing
book.borrow(memberId, dueDate);
member.borrowBook(isbn);
console.log(`๐ ${member.name} borrowed "${book.title}"`);
console.log(`๐
Due date: ${dueDate.toDateString()}`);
return true;
}
// ๐ Return book
returnBook(memberId: string, isbn: string): boolean {
const member = this.members.get(memberId);
const book = this.books.get(isbn);
if (!member || !book) {
console.log('โ Member or book not found');
return false;
}
if (book.getBorrower() !== memberId) {
console.log('โ This book was not borrowed by this member');
return false;
}
// Process return
book.return();
member.returnBook(isbn);
console.log(`โ
${member.name} returned "${book.title}"`);
return true;
}
// ๐ Search books
searchBooks(query: string): Book[] {
const results: Book[] = [];
for (const book of this.books.values()) {
if (book.title.toLowerCase().includes(query.toLowerCase()) ||
book.author.toLowerCase().includes(query.toLowerCase()) ||
book.genre.toLowerCase().includes(query.toLowerCase())) {
results.push(book);
}
}
return results;
}
// ๐ Display library stats
displayStats(): void {
console.log('\n๐๏ธ Library Statistics');
console.log('='.repeat(25));
console.log(`๐ Total books: ${this.books.size}`);
console.log(`๐ฅ Total members: ${this.members.size}`);
let availableBooks = 0;
let borrowedBooks = 0;
for (const book of this.books.values()) {
if (book.getStatus() === 'available') {
availableBooks++;
} else {
borrowedBooks++;
}
}
console.log(`โ
Available books: ${availableBooks}`);
console.log(`๐ Borrowed books: ${borrowedBooks}`);
}
// ๐ List all books
listBooks(): void {
console.log('\n๐ Library Catalog:');
console.log('='.repeat(30));
for (const book of this.books.values()) {
console.log(book.getInfo());
if (book.getStatus() === 'borrowed') {
const member = this.members.get(book.getBorrower()!);
const dueDate = book.getDueDate()!;
console.log(` ๐
Due: ${dueDate.toDateString()} (${member?.name})`);
}
}
}
}
// ๐ Let's build our library!
const library = new Library();
// ๐ Add some books
const books = [
new Book('978-0134685991', 'Effective TypeScript', 'Dan Vanderkam', 'Programming'),
new Book('978-1491927632', 'Programming TypeScript', 'Boris Cherny', 'Programming'),
new Book('978-0596517748', 'JavaScript: The Good Parts', 'Douglas Crockford', 'Programming'),
new Book('978-0321125217', 'Domain-Driven Design', 'Eric Evans', 'Software Design')
];
books.forEach(book => library.addBook(book));
// ๐ฅ Register members
const members = [
new LibraryMember('MEM001', 'Alice Johnson', '[email protected]'),
new LibraryMember('MEM002', 'Bob Smith', '[email protected]'),
new LibraryMember('MEM003', 'Carol Davis', '[email protected]')
];
members.forEach(member => library.registerMember(member));
// ๐ Test borrowing
library.borrowBook('MEM001', '978-0134685991');
library.borrowBook('MEM002', '978-1491927632');
// ๐ Display current state
library.displayStats();
library.listBooks();
// ๐ Search functionality
console.log('\n๐ Search results for "TypeScript":');
const searchResults = library.searchBooks('TypeScript');
searchResults.forEach(book => console.log(book.getInfo()));
๐ Key Takeaways
Congratulations! Youโve mastered TypeScript classes! Hereโs what you can now do:
- โ Create classes with properties and methods ๐๏ธ
- โ Use constructors to initialize objects properly ๐ง
- โ Apply access modifiers for proper encapsulation ๐
- โ Build real-world applications with OOP principles ๐
- โ Organize code into reusable, maintainable structures ๐
Remember: Classes are your building blocks for creating amazing applications! ๐
๐ค Next Steps
Amazing work! ๐ Youโve unlocked the power of TypeScript classes!
Hereโs your learning path forward:
- ๐ป Practice with the library management exercise above
- ๐๏ธ Try building your own class-based project
- ๐ Next tutorial: Class Properties and Methods - Building Class Members
- ๐ Share your class designs with the community!
Remember: Great applications start with well-designed classes. Keep building, keep learning! ๐
Happy coding! ๐๐๏ธโจ