Class và OOP trong TypeScript

Bài 11 – Class, constructor, thuộc tính, phương thức và OOP trong TypeScript theo cách đơn giản, trực quan và tiêu chuẩn.

11/1/2024 DaiPhan
Bài 9 / 17

Class và Lập trình hướng đối tượng trong TypeScript

TypeScript bổ sung typing vào mô hình OOP của JavaScript, giúp class rõ ràng, an toàn và mạnh mẽ hơn. Đây là nền tảng quan trọng khi xây dựng mô hình dữ liệu, xử lý nghiệp vụ hoặc cấu trúc ứng dụng lớn.


2. Nội dung chính

  • Class và cách khai báo class
  • Constructor và thuộc tính
  • Access modifiers: public, private, protected
  • Kế thừa (extends) và ghi đè (override)
  • Getter – Setter
  • Static property và static method
  • Ứng dụng OOP trong dự án thực tế

3. Ví dụ chi tiết

3.1. Class cơ bản

class User {
  public id: number;
  public name: string;
  private password: string;

  constructor(id: number, name: string, password: string) {
    this.id = id;
    this.name = name;
    this.password = password;
  }

  greet() {
    console.log(`Hello, ${this.name}`);
  }

  private encryptPassword() {
    return `***${this.password}***`;
  }

  // Public method để truy cập private method
  getEncryptedPassword() {
    return this.encryptPassword();
  }
}

const u = new User(1, "Alice", "123456");
u.greet(); // Hello, Alice
console.log(u.name); // Alice
// u.password; // ❌ Error: private property

3.2. Kế thừa class

class Admin extends User {
  public role: string;

  constructor(id: number, name: string, password: string, role: string) {
    super(id, name, password);
    this.role = role;
  }

  // Override method
  greet() {
    console.log(`Admin ${this.name} with role: ${this.role}`);
  }

  // Thêm method mới
  manageSystem() {
    console.log(`${this.name} is managing the system`);
  }
}

const admin = new Admin(2, "Bob", "pass123", "superadmin");
admin.greet(); // Admin Bob with role: superadmin
admin.manageSystem(); // Bob is managing the system

3.3. Getter – Setter

class Product {
  constructor(private _price: number) {}

  get price() {
    return this._price;
  }

  set price(value: number) {
    if (value < 0) throw new Error("Invalid price");
    this._price = value;
  }

  get formattedPrice() {
    return `$${this._price.toFixed(2)}`;
  }
}

const p = new Product(100);
console.log(p.price); // 100
console.log(p.formattedPrice); // $100.00

p.price = 25; // ✅ Valid
// p.price = -10; // ❌ Error: Invalid price

3.4. Static property – method

class MathUtil {
  static PI = 3.14159;
  static E = 2.71828;

  static sum(a: number, b: number): number {
    return a + b;
  }

  static circleArea(radius: number): number {
    return this.PI * radius * radius;
  }

  static randomBetween(min: number, max: number): number {
    return Math.random() * (max - min) + min;
  }
}

console.log(MathUtil.PI); // 3.14159
console.log(MathUtil.sum(3, 4)); // 7
console.log(MathUtil.circleArea(5)); // 78.53975
console.log(MathUtil.randomBetween(1, 10)); // Random number

3. Kiến thức trọng tâm

3.1. Access modifiers giúp kiểm soát quyền truy cập

ModifierTruy cập trong classTruy cập trong subclassTruy cập từ bên ngoài
public
protected
private

💡 GHI NHỚ: Mặc định là public nếu không chỉ định

3.2. Kế thừa và ghi đè giúp tái sử dụng và mở rộng hành vi

  • Hữu ích khi xây dựng mô hình: User → Admin, Product → DigitalProduct
  • Ghi đè (override) cho phép thay đổi hành vi của parent class
  • super() gọi constructor của parent class
  • super.method() gọi method của parent class

3.3. Static dùng cho logic “thuộc về class”, không thuộc về instance

  • Hàm tiện ích: MathUtil.sum(), DateUtil.format()
  • Hằng số: MathUtil.PI, AppConfig.API_URL
  • Factory methods: User.createAdmin(), Product.createDefault()
  • Không cần tạo instance để sử dụng

4. Bài tập thực hành

Bài 1: Tạo class Customer

class Customer {
  constructor(
    public id: number,
    public name: string,
    public email: string
  ) {}

  greet() {
    console.log(`Hello ${this.name}, your email is ${this.email}`);
  }

  updateEmail(newEmail: string) {
    this.email = newEmail;
  }
}

const customer = new Customer(1, "John Doe", "john@example.com");
customer.greet(); // Hello John Doe, your email is john@example.com

Bài 2: Tạo class VIPCustomer kế thừa Customer

class VIPCustomer extends Customer {
  constructor(
    id: number,
    name: string,
    email: string,
    public level: "bronze" | "silver" | "gold" | "platinum",
    private discount: number = 0
  ) {
    super(id, name, email);
  }

  getDiscount() {
    return this.discount;
  }

  override greet() {
    console.log(`VIP Customer ${this.name} - Level: ${this.level}`);
  }

  calculatePrice(originalPrice: number) {
    return originalPrice * (1 - this.discount);
  }
}

const vip = new VIPCustomer(2, "Jane Smith", "jane@example.com", "gold", 0.15);
vip.greet(); // VIP Customer Jane Smith - Level: gold
console.log(vip.calculatePrice(100)); // 85 (15% discount)

Bài 3: Tạo class Circle với static methods

class Circle {
  static PI = 3.14159265359;

  constructor(public radius: number) {}

  getArea(): number {
    return Circle.PI * this.radius * this.radius;
  }

  getCircumference(): number {
    return 2 * Circle.PI * this.radius;
  }

  static areaFromRadius(radius: number): number {
    return this.PI * radius * radius;
  }

  static circumferenceFromRadius(radius: number): number {
    return 2 * this.PI * radius;
  }
}

const circle = new Circle(5);
console.log(circle.getArea()); // 78.53981633975
console.log(circle.getCircumference()); // 31.4159265359

console.log(Circle.areaFromRadius(10)); // 314.159265359
console.log(Circle.circumferenceFromRadius(10)); // 62.8318530718

5. Sai lầm thường gặp

  • Không sử dụng access modifiers: Làm code dễ bị thay đổi từ bên ngoài
  • Dùng static cho mọi thứ: Static không phù hợp cho state của object
  • Kế thừa quá nhiều cấp: Làm code khó hiểu và bảo trì
  • Không hiểu về this: Gây lỗi khi method bị gọi trong context khác
  • Quên gọi super(): Gây lỗi khi kế thừa class

⚠️ GHI NHỚ: Composition thường tốt hơn inheritance


6. Kết luận

Class trong TypeScript mạnh hơn JavaScript nhờ typing, access modifiers và khả năng mở rộng rõ ràng. Đây là nền tảng quan trọng khi bạn xây dựng mô hình dữ liệu và nghiệp vụ trong dự án lớn.

🔑 GHI NHỚ QUAN TRỌNG:

  • Sử dụng access modifiers để bảo vệ dữ liệu
  • Kế thừa đúng cách với extends và super
  • Getter/Setter cho validation và encapsulation
  • Static cho utility functions và constants
  • Hiểu về this binding để tránh lỗi runtime
17 bài học
Bài 9
Tiến độ hoàn thành 53%

Đã hoàn thành 9/17 bài học