TypeScript 类(长文讲解)

什么是 TypeScript 类?

在 JavaScript 的世界里,我们常用函数和对象来组织代码。但当项目变得复杂时,这种写法容易导致代码难以维护。TypeScript 为解决这个问题引入了“类”这个概念,它让面向对象编程变得更加清晰、安全和可扩展。

你可以把 TypeScript 类想象成一个“模板”或“蓝图”。就像建筑师会先画一张建筑图纸,再根据图纸盖房子一样,类就是定义对象结构的蓝图。有了类,我们就能批量创建具有相同属性和方法的对象,而不会重复写一堆重复的代码。

TypeScript 类不仅支持传统的面向对象特性(如封装、继承、多态),还通过类型系统增强了代码的可读性和可维护性。比如,你可以在定义类的时候就明确告诉 TypeScript:这个属性必须是字符串,那个方法返回的是数字。

这就像你给快递包裹贴上标签——“易碎品”“请勿倒置”——这样无论是你自己还是别人,都能清楚地知道该如何处理。类型系统就是这样的标签系统,它帮助我们在开发阶段就发现潜在错误。


类的基本语法与结构

TypeScript 类的语法非常直观,和 Java、C# 等语言很相似。下面我们来创建一个最简单的类。

class Person {
  // 属性(字段)
  name: string;
  age: number;

  // 构造函数
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  // 方法
  introduce(): string {
    return `你好,我叫 ${this.name},今年 ${this.age} 岁。`;
  }
}

逐行解释

  • class Person:声明一个名为 Person 的类,它是所有人类对象的模板。
  • name: stringage: number:定义两个属性,分别表示姓名和年龄,类型明确为字符串和数字。
  • constructor:构造函数,用于在创建对象实例时初始化属性。每次 new Person(...) 被调用时,这个函数就会执行一次。
  • this.name = namethis 指向当前实例对象,把传入的参数赋值给对象的属性。
  • introduce() 方法:返回一段自我介绍的文字,使用了模板字符串(ES6 特性)。

现在我们来用这个类创建一个真实的人:

const person1 = new Person("小明", 25);
console.log(person1.introduce()); // 输出:你好,我叫 小明,今年 25 岁。

这样,我们就从“蓝图”造出了一个“人”。这个过程叫做实例化,new 关键字就是用来生成实例的。


属性修饰符:控制访问权限

TypeScript 类支持三种访问修饰符,它们像“门锁”一样控制属性和方法的可见性。

修饰符 含义 可见范围
public 公开的 所有地方都能访问(默认)
private 私有的 只能在类内部访问
protected 受保护的 类内部和子类中可访问

示例:使用 private 限制访问

class BankAccount {
  private balance: number; // 私有属性:外部无法直接读写

  constructor(initialBalance: number) {
    this.balance = initialBalance;
  }

  // 公共方法:控制如何查看余额
  getBalance(): number {
    return this.balance;
  }

  // 公共方法:控制如何存钱
  deposit(amount: number): void {
    if (amount > 0) {
      this.balance += amount;
      console.log(`成功存入 ${amount} 元,当前余额为 ${this.balance} 元`);
    } else {
      console.log("存款金额必须大于 0");
    }
  }

  // 公共方法:控制如何取钱
  withdraw(amount: number): void {
    if (amount > this.balance) {
      console.log("余额不足,无法取款");
    } else {
      this.balance -= amount;
      console.log(`成功取出 ${amount} 元,当前余额为 ${this.balance} 元`);
    }
  }
}

// 使用示例
const account = new BankAccount(1000);
account.deposit(500);     // 成功存入 500 元,当前余额为 1500 元
account.withdraw(200);    // 成功取出 200 元,当前余额为 1300 元
console.log(account.getBalance()); // 1300
// account.balance = 9999; // ❌ 编译错误!不能直接访问私有属性

💡 小贴士:private 能防止外部随意修改关键数据,比如银行账户余额。这就像你的手机密码,只有你自己知道,别人不能随便改。


构造函数与参数属性简化

当类的属性和构造函数参数一致时,可以使用“参数属性”语法来简化代码。

传统写法 vs 简化写法

// 传统方式:先声明属性,再在构造函数中赋值
class Point {
  x: number;
  y: number;

  constructor(x: number, y: number) {
    this.x = x;
    this.y = y;
  }
}
// 简化方式:直接在构造函数参数前加修饰符
class Point {
  constructor(
    public x: number,  // 相当于声明了 x 属性,并赋值
    public y: number   // 相当于声明了 y 属性,并赋值
  ) {}
}

效果完全一样,但代码更简洁。这种写法在实际项目中非常常见,尤其适合用于 DTO(数据传输对象)或配置类。


继承与多态:构建类的层次结构

在现实世界中,我们经常看到“父子关系”。比如“汽车”是“交通工具”的一种,“电动车”又是“汽车”的一种。TypeScript 类也支持这种继承机制。

基类与子类

// 基类:交通工具
class Vehicle {
  protected speed: number = 0;

  start(): void {
    console.log("交通工具启动中...");
  }

  stop(): void {
    console.log("交通工具已停止");
  }

  // 抽象方法:子类必须实现
  abstract getSpeed(): number;
}

// 子类:汽车
class Car extends Vehicle {
  private wheels: number = 4;

  getSpeed(): number {
    return this.speed;
  }

  accelerate(): void {
    this.speed += 10;
    console.log(`汽车加速,当前速度:${this.speed} km/h`);
  }

  // 重写父类方法
  start(): void {
    console.log("汽车引擎启动,准备出发!");
  }
}

// 子类:自行车
class Bicycle extends Vehicle {
  private wheels: number = 2;

  getSpeed(): number {
    return this.speed;
  }

  pedal(): void {
    this.speed += 5;
    console.log(`自行车踩踏,当前速度:${this.speed} km/h`);
  }

  start(): void {
    console.log("自行车骑上,准备出发!");
  }
}

使用继承的实例

const car = new Car();
const bike = new Bicycle();

car.start();        // 输出:汽车引擎启动,准备出发!
car.accelerate();   // 输出:汽车加速,当前速度:10 km/h

bike.start();       // 输出:自行车骑上,准备出发!
bike.pedal();       // 输出:自行车踩踏,当前速度:5 km/h

🌟 多态的魅力在于:你可以统一用父类类型来引用子类对象,而程序会自动调用对应的方法。

const vehicles: Vehicle[] = [car, bike];

vehicles.forEach(v => {
  v.start(); // 根据实际类型调用不同版本的 start()
});

这就像你对“交通工具”说“启动”,不管是汽车还是自行车,它们都会按自己的方式启动。


静态成员与单例模式

有时我们需要一些“属于类本身”的属性或方法,而不是属于某个实例。这就需要用到 static 关键字。

静态属性与方法

class MathUtils {
  // 静态属性:类级别的常量
  static PI: number = 3.14159;

  // 静态方法:可以直接通过类名调用,无需创建实例
  static square(x: number): number {
    return x * x;
  }

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

// 使用静态成员
console.log(MathUtils.PI);           // 3.14159
console.log(MathUtils.square(4));    // 16
console.log(MathUtils.add(3, 7));    // 10

单例模式:确保只有一个实例

class Database {
  private static instance: Database | null = null;

  private constructor() {
    console.log("数据库连接已建立");
  }

  // 获取唯一实例
  static getInstance(): Database {
    if (Database.instance === null) {
      Database.instance = new Database();
    }
    return Database.instance;
  }

  query(sql: string): void {
    console.log(`执行 SQL: ${sql}`);
  }
}

// 使用单例
const db1 = Database.getInstance();
const db2 = Database.getInstance();

console.log(db1 === db2); // true,是同一个实例
db1.query("SELECT * FROM users"); // 执行 SQL: SELECT * FROM users

✅ 单例模式常用于数据库连接、日志记录器、配置管理器等场景,避免重复创建资源。


实战项目:构建一个简易的学生管理系统

让我们用 TypeScript 类来构建一个真实可用的系统。

class Student {
  private id: string;
  private name: string;
  private grades: number[] = [];

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

  // 添加成绩
  addGrade(grade: number): void {
    if (grade < 0 || grade > 100) {
      console.log("成绩必须在 0 到 100 之间");
      return;
    }
    this.grades.push(grade);
  }

  // 计算平均分
  getAverage(): number {
    if (this.grades.length === 0) return 0;
    const sum = this.grades.reduce((a, b) => a + b, 0);
    return Math.round((sum / this.grades.length) * 100) / 100;
  }

  // 获取信息
  getInfo(): string {
    return `学号:${this.id},姓名:${this.name},平均分:${this.getAverage()}`;
  }
}

// 使用示例
const student = new Student("S001", "张三");
student.addGrade(85);
student.addGrade(92);
student.addGrade(78);
console.log(student.getInfo()); // 学号:S001,姓名:张三,平均分:85

这个系统虽然简单,但体现了类的核心优势:封装数据和行为,提高代码复用性


总结

TypeScript 类是构建复杂应用的基石。它让代码更清晰、更安全、更容易维护。从基础语法到访问控制,再到继承、静态成员和设计模式,每一步都为你提供更强大的编程能力。

无论是开发前端应用、后端服务,还是构建工具库,掌握 TypeScript 类都能让你的代码质量跃上一个台阶。它不只是语法糖,更是一种思维方式的升级。

当你开始用类来组织代码时,你就不再是“写代码”,而是在“设计系统”。这正是现代 JavaScript 开发的真正魅力所在。

如果你正在学习 TypeScript,不妨从一个简单的 Person 类开始,一步步构建你的第一个“对象世界”。