TypeScript adds type annotations and access modifiers to JavaScript classes.
Basic Class
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
greet(): string {
return `Hello, I'm ${this.name} and I'm ${this.age} years old.`;
}
}
const person = new Person("Alice", 25);
console.log(person.greet());
Access Modifiers
public (default)
Properties and methods are accessible from anywhere.
class Person {
public name: string; // public is optional (default)
public age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
private
Only accessible within the class.
class BankAccount {
private balance: number = 0;
deposit(amount: number): void {
this.balance += amount;
}
getBalance(): number {
return this.balance;
}
}
const account = new BankAccount();
account.deposit(100);
// account.balance; // ❌ Error: Property 'balance' is private
console.log(account.getBalance()); // ✅ OK
protected
Accessible within the class and subclasses.
class Animal {
protected name: string;
constructor(name: string) {
this.name = name;
}
}
class Dog extends Animal {
bark(): void {
console.log(`${this.name} barks!`); // ✅ OK, name is protected
}
}
const dog = new Dog("Buddy");
// dog.name; // ❌ Error: Property 'name' is protected
readonly
Properties that can only be set during initialization.
class Person {
readonly id: number;
name: string;
constructor(id: number, name: string) {
this.id = id; // ✅ OK, setting during initialization
this.name = name;
}
}
const person = new Person(1, "Alice");
// person.id = 2; // ❌ Error: Cannot assign to 'id' because it is a read-only property
Parameter Properties
Shorthand for declaring and initializing properties.
// Long form
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// Short form with parameter properties
class Person {
constructor(public name: string, public age: number) {
// Automatically creates and assigns this.name and this.age
}
}
Getters and Setters
class Temperature {
private _celsius: number = 0;
get celsius(): number {
return this._celsius;
}
set celsius(value: number) {
if (value < -273.15) {
throw new Error("Temperature cannot be below absolute zero");
}
this._celsius = value;
}
get fahrenheit(): number {
return this._celsius * 9/5 + 32;
}
set fahrenheit(value: number) {
this.celsius = (value - 32) * 5/9;
}
}
const temp = new Temperature();
temp.celsius = 25;
console.log(temp.fahrenheit); // 77
Inheritance
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
move(distance: number = 0): void {
console.log(`${this.name} moved ${distance}m.`);
}
}
class Dog extends Animal {
breed: string;
constructor(name: string, breed: string) {
super(name); // Call parent constructor
this.breed = breed;
}
bark(): void {
console.log("Woof! Woof!");
}
}
const dog = new Dog("Buddy", "Golden Retriever");
dog.move(10); // Inherited method
dog.bark(); // Dog-specific method
Abstract Classes
Cannot be instantiated directly, must be extended.
abstract class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
abstract makeSound(): void; // Must be implemented by subclasses
move(): void {
console.log(`${this.name} is moving.`);
}
}
class Dog extends Animal {
makeSound(): void {
console.log("Woof!");
}
}
// const animal = new Animal("Generic"); // ❌ Error: Cannot create instance of abstract class
const dog = new Dog("Buddy");
dog.makeSound();
Static Members
Belong to the class itself, not instances.
class MathHelper {
static PI: number = 3.14159;
static add(a: number, b: number): number {
return a + b;
}
}
console.log(MathHelper.PI); // 3.14159
console.log(MathHelper.add(5, 3)); // 8
// const helper = new MathHelper();
// helper.PI; // ❌ Error: Property 'PI' does not exist on type 'MathHelper'
Implementing Interfaces
Classes can implement interfaces.
interface Flyable {
fly(): void;
}
interface Swimmable {
swim(): void;
}
class Duck implements Flyable, Swimmable {
fly(): void {
console.log("Flying...");
}
swim(): void {
console.log("Swimming...");
}
}
Exercises
Exercise 1: Basic Class
// Create a class called 'Car' with:
// - make (string)
// - model (string)
// - year (number)
// - Constructor that initializes all properties
// - Method 'getInfo()' that returns a string with car details
Exercise 2: Access Modifiers
// Create a class called 'BankAccount' with:
// - private balance (number, initialized to 0)
// - public deposit(amount: number): void
// - public withdraw(amount: number): void
// - public getBalance(): number
Exercise 3: Inheritance
// Create a base class 'Shape' with:
// - protected name (string)
// - abstract method area(): number
// Then create 'Circle' that extends 'Shape' with:
// - private radius (number)
// - Implements area() method
Exercise 4: Getters and Setters
// Create a class 'Rectangle' with:
// - private width and height
// - getter and setter for width (validate > 0)
// - getter and setter for height (validate > 0)
// - getter for area (width * height)
Exercise 5: Static Members
// Create a class 'StringUtils' with:
// - static method reverse(str: string): string
// - static method capitalize(str: string): string
// - static method isEmpty(str: string): boolean
Best Practices
- Use access modifiers - Encapsulate your data properly
- Prefer private over public - Only expose what's necessary
- Use readonly for immutable properties - When values shouldn't change
- Use abstract classes for base functionality - When you want shared implementation
- Use interfaces for contracts - When you only need structure
Common Mistakes
- Forgetting to call super() in derived classes
// ❌ Bad
class Dog extends Animal {
constructor(name: string) {
// Missing super() call
this.name = name;
}
}
- Using public when private would work
// ❌ Bad
class BankAccount {
public balance: number; // Should be private
}
// ✅ Good
class BankAccount {
private balance: number;
}
- Not using parameter properties
// ❌ Verbose
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
// ✅ Concise
class Person {
constructor(public name: string, public age: number) {}
}
Next Steps
Learn about Arrays and Tuples.