TypeScript: TypeScript 类 (class)

最后更新:2026-08-26

类(class)是 JavaScript ES6 引入的面向对象语法,TypeScript 在此基础上添加了类型注解和访问控制——让类的属性和方法都有类型约束。

1. 类的基本语法

(1) 属性声明与构造函数

TypeScript 要求类的属性必须先声明再使用——这和 JavaScript 不同:

TYPESCRIPT
class User {
  // 属性声明(TypeScript 要求)
  name: string;
  age: number;

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

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

let user = new User("Charlie", 20);
console.log(user.greet());  // "你好,我是Charlie,今年20岁"

(2) 属性初始化的几种方式

TYPESCRIPT
class Config {
  // 方式一:声明时直接初始化
  host: string = "localhost";
  port: number = 3000;

  // 方式二:构造函数中初始化
  env: string;

  constructor(env: string) {
    this.env = env;
  }

  // 方式三:断言非空(!)——你保证稍后会赋值
  data!: string;
}

let cfg = new Config("development");
cfg.data = "some data";  // 先创建再赋值
🔥 易错: 属性既不初始化也不在构造函数中赋值,TypeScript 会报"属性没有初始化"错误(strict 模式下)。用 ! 断言可以绕过,但要确保真的会赋值。


2. 访问修饰符

TypeScript 的三种访问修饰符控制属性和方法的可见性:

修饰符 类内部 子类 类外部
public
protected
private

(1) public(默认)

TYPESCRIPT
class Person {
  public name: string;   // public 是默认值,可省略
  public age: number;

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

let p = new Person("Charlie", 20);
console.log(p.name);  // ✅ 类外部可以访问
p.name = "Diana";      // ✅ 类外部可以修改

(2) private

TYPESCRIPT
class BankAccount {
  private balance: number;

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

  public deposit(amount: number): void {
    if (amount > 0) this.balance += amount;
  }

  public withdraw(amount: number): boolean {
    if (amount > 0 && amount <= this.balance) {
      this.balance -= amount;
      return true;
    }
    return false;
  }

  public getBalance(): number {
    return this.balance;
  }
}

let account = new BankAccount(1000);
account.deposit(500);
account.withdraw(200);
console.log(account.getBalance());  // 1300
// account.balance;                  // ❌ private 属性外部不可访问
// account.balance = 99999;          // ❌ private 属性外部不可修改

(3) protected

TYPESCRIPT
class Animal {
  protected name: string;

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

  protected makeSound(sound: string): void {
    console.log(`${this.name}:${sound}`);
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name);
  }

  public bark(): void {
    this.makeSound("汪汪!");  // ✅ 子类可以访问 protected 方法
    // console.log(this.name);  // ✅ 子类可以访问 protected 属性
  }
}

let dog = new Dog("旺财");
dog.bark();           // "旺财:汪汪!"
// dog.name;           // ❌ protected 属性外部不可访问
// dog.makeSound("嗷"); // ❌ protected 方法外部不可调用

(4) 构造函数参数的简写

TypeScript 提供了参数属性(parameter properties)——在构造函数参数前加修饰符,自动声明并初始化属性:

TYPESCRIPT
// 完整写法
class User1 {
  public name: string;
  private age: number;

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

// 简写——效果完全相同
class User2 {
  constructor(
    public name: string,
    private age: number
  ) {}
}

let u = new User2("Charlie", 20);
console.log(u.name);    // ✅ public
// console.log(u.age);  // ❌ private
💡 建议: 参数属性让代码更简洁,但属性多时可读性下降。3个以内推荐简写,超过3个建议显式声明。


3. readonly 修饰符

readonly 修饰的属性只能在声明时或构造函数中赋值:

TYPESCRIPT
class Circle {
  readonly radius: number;

  constructor(radius: number) {
    this.radius = radius;  // ✅ 构造函数中可以赋值
  }

  get area(): number {
    return Math.PI * this.radius ** 2;
  }
}

let circle = new Circle(5);
console.log(circle.area);     // 78.54
// circle.radius = 10;        // ❌ readonly 属性不可修改

(1) readonly + 参数属性

TYPESCRIPT
class Config {
  constructor(
    readonly host: string,
    readonly port: number
  ) {}
}

let cfg = new Config("localhost", 3000);
console.log(`${cfg.host}:${cfg.port}`);  // "localhost:3000"
// cfg.host = "other";  // ❌ readonly

4. 存取器(getter / setter)

存取器让你在读写属性时执行自定义逻辑:

(1) 基本语法

TYPESCRIPT
class Employee {
  private _salary: number = 0;

  // getter——读取时执行
  get salary(): number {
    return this._salary;
  }

  // setter——赋值时执行验证
  set salary(value: number) {
    if (value < 0) {
      throw new Error("工资不能为负数");
    }
    this._salary = value;
  }
}

let emp = new Employee();
emp.salary = 8000;       // 调用 setter
console.log(emp.salary);  // 调用 getter → 8000
// emp.salary = -100;     // ❌ 抛出错误

(2) 只读属性(只有 getter 没有 setter)

TYPESCRIPT
class User {
  constructor(
    private firstName: string,
    private lastName: string
  ) {}

  get fullName(): string {
    return `${this.firstName} ${this.lastName}`;
  }
  // 没有 setter → fullName 是只读的
}

let user = new User("三", "张");
console.log(user.fullName);  // "三 张"
// user.fullName = "四 李";  // ❌ 没有 setter

▶ 示例:温度转换器

TYPESCRIPT
class Temperature {
  private _celsius: number = 0;

  constructor(celsius: number) {
    this._celsius = celsius;
  }

  get celsius(): number {
    return this._celsius;
  }

  set celsius(value: number) {
    if (value < -273.15) {
      throw new Error("温度不能低于绝对零度(-273.15°C)");
    }
    this._celsius = value;
  }

  get fahrenheit(): number {
    return this._celsius * 9 / 5 + 32;
  }

  set fahrenheit(value: number) {
    this.celsius = (value - 32) * 5 / 9;  // 复用 celsius 的验证
  }
}

let temp = new Temperature(100);
console.log(`${temp.celsius}°C = ${temp.fahrenheit}°F`);  // "100°C = 212°F"

temp.fahrenheit = 32;
console.log(`${temp.celsius}°C = ${temp.fahrenheit}°F`);  // "0°C = 32°F"
▶ 试一试

输出:

TEXT 📖 仅展示
100°C = 212°F
0°C = 32°F

5. 静态成员

static 修饰的属性和方法属于类本身,不属于实例:

(1) 静态属性和方法

TYPESCRIPT
class MathUtils {
  static PI: number = 3.14159;

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

  static clamp(value: number, min: number, max: number): number {
    return Math.min(Math.max(value, min), max);
  }
}

// 不需要创建实例,直接通过类名调用
console.log(MathUtils.PI);              // 3.14159
console.log(MathUtils.circleArea(5));   // 78.53975
console.log(MathUtils.clamp(150, 0, 100));  // 100

(2) 静态块(TypeScript 5.0 / ES2022+)

TYPESCRIPT
class Config {
  static host: string;
  static port: number;

  // 静态初始化块
  static {
    Config.host = process.env.HOST ?? "localhost";
    Config.port = Number(process.env.PORT) || 3000;
  }
}

(3) 静态成员与实例成员的区别

TYPESCRIPT
class Counter {
  static totalCount: number = 0;   // 所有实例共享
  instanceCount: number = 0;       // 每个实例独立

  increment(): void {
    Counter.totalCount++;
    this.instanceCount++;
  }
}

let c1 = new Counter();
let c2 = new Counter();

c1.increment();
c1.increment();
c2.increment();

console.log(`实例1:${c1.instanceCount}`);  // 2
console.log(`实例2:${c2.instanceCount}`);  // 1
console.log(`总计:${Counter.totalCount}`); // 3

❓ 常见问题

Q private 在运行时真的私有吗?
A 不是。TypeScript 的 private 只是编译时检查——编译后的 JavaScript 代码中,private 属性仍然可以访问。ES2022 的 #private 语法(私有字段)才是运行时真正的私有。TypeScript 也支持 #privateclass C { #secret = 1; }
Q 什么时候用 private,什么时候用 protected?
A private 用于"只有这个类自己需要"的内部实现细节;protected 用于"子类也可能需要访问或覆写"的成员。如果不确定,先用 private——需要时再改为 protected(放宽比收紧安全)。
Q getter/setter 和普通方法有什么区别?
A getter/setter 让属性的读写看起来像直接访问(obj.name),但底层执行自定义逻辑。普通方法需要显式调用(obj.getName())。对外暴露的属性建议用 getter/setter(可以做验证、计算属性),内部实现用普通方法。
Q 静态方法能用 this 吗?
A 静态方法中的 this 指向类本身(不是实例)。static create() { return new this(); }this 等于当前类。但在箭头函数或回调中 this 可能丢失——静态方法中推荐用类名替代 this

📖 小节

📝 作业

  1. 基础题(难度⭐):创建一个 Rectangle 类(width、height),实现 getArea()getPerimeter() 方法。用参数属性简化构造函数。
  2. 进阶题(难度⭐⭐):创建一个 BankAccount 类——balance 为 private,通过 deposit/withdraw 方法操作,用 getter 暴露只读余额,withdraw 时验证余额是否充足。
  3. 挑战题(难度⭐⭐⭐):实现一个 Stack<T> 泛型类——用 private 数组存储数据,提供 push/pop/peek/size 方法,用 getter 实现 size 属性(只读),用静态方法 static fromArray<T>(items: T[]): Stack<T> 从数组创建栈。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏