Bài 6. Lớp và lập trình hướng đối tượng

TypeScript bổ sung cho lớp JavaScript các công cụ kiểm soát truy cập, lớp trừu tượng và cài đặt interface, giúp tổ chức mã hướng đối tượng chặt chẽ.

45 phútTrung cấp
Bạn sẽ học được
  • Khai báo lớp có kiểu thuộc tính
  • Dùng public, private, protected, readonly và #private
  • Kế thừa, lớp trừu tượng, implements
  • Biết khi nào nên dùng lớp, khi nào dùng hàm
Thuật ngữ cần biết
Access modifier
Từ khóa kiểm soát truy cập: public, private, protected.
Parameter property
Khai báo thuộc tính ngay trong tham số constructor.
Abstract class
Lớp không tạo đối tượng trực tiếp, chứa phương thức trừu tượng.
implements
Lớp cam kết cài đặt một interface.

Khái niệm và lý thuyết

Lớp gói dữ liệu (thuộc tính) và hành vi (phương thức). Bốn nguyên lý OOP: đóng gói, kế thừa, đa hình, trừu tượng. TS hỗ trợ đủ cả bốn ở mức kiểu.

Lưu ý private của TS chỉ kiểm tra lúc biên dịch; còn #ten là private thật của JavaScript, ẩn cả khi chạy. Trong mã mới nên dùng # cho dữ liệu cần bảo vệ thực sự.

Lớp cơ bản

class TaiKhoan {
  #soDu = 0;                         // private thật (runtime)
  constructor(public readonly chuTK: string) {}
  napTien(soTien: number): void {
    if (soTien <= 0) throw new Error("Số tiền không hợp lệ");
    this.#soDu += soTien;
  }
  get soDu(): number { return this.#soDu; }
}
const tk = new TaiKhoan("An");
tk.napTien(500_000);
console.log(tk.soDu);

Kế thừa và trừu tượng

abstract class Hinh {
  abstract dienTich(): number;
  moTa() { return `Diện tích: ${this.dienTich().toFixed(2)}`; }
}
class Tron extends Hinh {
  constructor(private r: number) { super(); }
  dienTich() { return Math.PI * this.r ** 2; }
}
class ChuNhat extends Hinh {
  constructor(private d: number, private r: number) { super(); }
  override dienTich() { return this.d * this.r; }
}
const ds: Hinh[] = [new Tron(1), new ChuNhat(2, 3)];
ds.forEach(h => console.log(h.moTa())); // đa hình

implements interface

interface LuuTru<T> {
  luu(khoa: string, gt: T): void;
  doc(khoa: string): T | undefined;
}
class BoNhoTam<T> implements LuuTru<T> {
  private kho = new Map<string, T>();
  luu(k: string, gt: T) { this.kho.set(k, gt); }
  doc(k: string) { return this.kho.get(k); }
}
Mẹo hay

Bật noImplicitOverride để bắt buộc ghi override khi ghi đè phương thức lớp cha, tránh vô tình ghi đè hoặc gõ sai tên.

Lưu ý

Đừng ép mọi thứ thành lớp. Với logic xử lý dữ liệu thuần, hàm và kiểu đối tượng thường gọn và dễ kiểm thử hơn.

Thực hành
  1. Tạo lớp SinhVien có điểm private, phương thức thêm điểm và getter điểm trung bình.
  2. Tạo lớp trừu tượng NhanVien với tinhLuong(); hai lớp con toàn thời gian và bán thời gian.
  3. Cài đặt interface LuuTru bằng localStorage.
  4. So sánh private và # bằng cách in đối tượng ra console.
Tóm tắt bài học
  • Lớp gói dữ liệu và hành vi.
  • Parameter property giúp constructor ngắn gọn.
  • abstract và implements tạo hợp đồng rõ ràng.
  • #private là private thật khi chạy.