TypeScript 类
TS 的类基于 ES6 的 class,但在它之上补齐了访问修饰符、参数属性、抽象类等面向对象的关键特性——这些都是纯 JS 没有的。这一章讲清楚 TS 类相比 JS 多出来的全部能力。如果你对 ES6 class 还不熟,建议先看 JS 系列的 ES6+ 一篇。
1. 类的基本写法
和 JS class 最大的一点不同:字段必须先声明类型,不能像 JS 那样在构造函数里直接 this.x = x 凭空创建:
// TS 的类基于 ES6,增加了类型标注
class Animal {
// 字段必须先声明类型(和 JS 不同)
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
speak(): string {
return `${this.name} 发出声音`;
}
}
const a = new Animal("小白", 3);
console.log(a.speak()); // 小白 发出声音2. 访问修饰符 public / private / protected
这是 TS 类相比 JS 最实用的增强。三个修饰符控制成员在哪里能被访问:
// 访问修饰符:public(默认) / private / protected
class BankAccount {
public owner: string; // 任何地方都能访问
private balance: number; // 只能在本类内部访问
protected id: string; // 本类 + 子类内部访问
constructor(owner: string, balance: number, id: string) {
this.owner = owner;
this.balance = balance;
this.id = id;
}
public deposit(amount: number): void {
this.balance += amount; // ✅ 类内部访问 private
}
public getBalance(): number {
return this.balance;
}
}
const acc = new BankAccount("小明", 1000, "A001");
console.log(acc.owner); // ✅ public 可访问
// console.log(acc.balance); // ❌ private 不可访问
// console.log(acc.id); // ❌ protected 不可访问public(默认):任何地方都能访问。private:只能在本类内部访问(包括构造函数)。protected:本类 + 子类内部可以访问。
注意:private 是编译期检查,运行时并不真正阻止访问(编译成 JS 后修饰符被擦除)。如果你需要运行时也私密,可以用 ES2022 的 #字段 私有字段语法(如 #balance)。
3. 参数属性:TS 的语法糖
这是一个非常省事的特性。在构造函数参数前加修饰符,TS 会自动帮你声明同名字段并赋值,省掉一大堆样板代码:
// 参数属性:构造函数参数加修饰符,自动声明并赋值
// 这是 TS 独有的语法糖,省掉大量样板代码
class User {
// 等价于:先声明字段 + 在构造函数里 this.x = x
constructor(
public name: string, // 自动创建 public name 字段
private age: number, // 自动创建 private age 字段
readonly id: number, // 自动创建只读 id 字段
) {}
}
const u = new User("小明", 20, 1);
console.log(u.name); // 小明
console.log(u.id); // 1
// u.id = 2; // ❌ readonly 不能改对比第 1 节那种"先声明字段 + 构造函数里赋值"的写法,参数属性让代码量减半。这是 TS 类最常用的语法糖。
4. readonly 与抽象类
两个常用的特性:readonly 让属性只读;abstract 定义抽象类(只能被继承,不能直接实例化):
// readonly:只读属性,只能在声明时或构造函数里赋值
class Config {
readonly version: string = "1.0.0";
readonly createdAt: Date;
constructor() {
this.createdAt = new Date(); // 构造函数里可以赋值
}
}
// 抽象类:只能被继承,不能直接实例化
abstract class Shape {
abstract area(): number; // 抽象方法:子类必须实现
describe(): void { // 普通方法:子类可以直接用
console.log(`面积是 ${this.area()}`);
}
}
class Circle extends Shape {
constructor(public radius: number) { super(); }
area(): number { // 子类必须实现抽象方法
return Math.PI * this.radius ** 2;
}
}
// const s = new Shape(); // ❌ 抽象类不能实例化
const c = new Circle(2);
c.describe(); // 面积是 12.566...抽象类的价值在于定义统一接口 + 复用部分实现:抽象方法强制子类各自实现,普通方法让子类共享。这是面向对象设计的重要工具。
5. implements:实现接口
类可以用 implements 声明自己实现了哪些接口——接口只约束"形状",不提供实现:
// implements:类实现接口(只约束形状,不提供实现)
interface Loggable {
log(msg: string): void;
}
class ConsoleLogger implements Loggable {
log(msg: string): void { // 必须实现 log 方法
console.log(msg);
}
}
// extends(继承)vs implements(实现):
// extends 复用父类的实现(单继承)
// implements 只遵守接口的形状(可多个)
class FileLogger extends ConsoleLogger implements Loggable {
// 既有 ConsoleLogger 的实现,又满足 Loggable 接口
}区分两个关键字:extends 是继承实现(子类拿到父类的代码),implements 是遵守契约(类必须自己实现接口规定的方法)。一个类只能 extends 一个父类,但可以 implements 多个接口。
6. 类也能用泛型
类同样可以带类型参数,这在泛型那一篇已经讲过,写法是 class Stack<T>。这里不再重复。
小结
TS 类 = ES6 class + 类型标注 + 访问修饰符 + 参数属性 + 抽象类 + 接口实现。最实用的是参数属性(省样板代码)和访问修饰符(封装)。下一篇讲 TS 的模块系统——import/export 怎么带类型。
← 上一篇 TypeScript 联合与交叉类型
下一篇 TypeScript 模块 →