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 也支持 #private:class 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。📖 小节
- TypeScript 类要求先声明属性再使用;属性可以通过声明初始化、构造函数赋值或
!断言 - 访问修饰符:public(默认,外部可访问)、protected(子类可访问)、private(仅类内部)
- 参数属性简化了属性声明+构造函数赋值的写法
- readonly 属性只能在声明时或构造函数中赋值
- getter/setter 在读写属性时执行自定义逻辑;只有 getter 没有 setter 则属性只读
- static 成员属于类本身,不属于实例;通过
类名.成员访问
📝 作业
- 基础题(难度⭐):创建一个
Rectangle类(width、height),实现getArea()和getPerimeter()方法。用参数属性简化构造函数。 - 进阶题(难度⭐⭐):创建一个
BankAccount类——balance 为 private,通过 deposit/withdraw 方法操作,用 getter 暴露只读余额,withdraw 时验证余额是否充足。 - 挑战题(难度⭐⭐⭐):实现一个
Stack<T>泛型类——用 private 数组存储数据,提供 push/pop/peek/size 方法,用 getter 实现 size 属性(只读),用静态方法static fromArray<T>(items: T[]): Stack<T>从数组创建栈。