TypeScript 开发指南 / 06 - 类
类
TypeScript 的类在 JavaScript 类的基础上增加了类型注解、访问修饰符和抽象类等特性。
基本类定义
class User {
// 属性声明
name: string;
age: number;
// 构造函数
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
// 方法
greet(): string {
return `你好,我是 ${this.name},今年 ${this.age} 岁。`;
}
}
const alice = new User("Alice", 25);
console.log(alice.greet());
简写构造函数(Parameter Properties)
// 简写:参数前加修饰符,自动声明并赋值属性
class User {
constructor(
public name: string,
public age: number,
private password: string,
readonly id: number
) {}
}
// 等价于
class User {
public name: string;
public age: number;
private password: string;
readonly id: number;
constructor(name: string, age: number, password: string, id: number) {
this.name = name;
this.age = age;
this.password = password;
this.id = id;
}
}
访问修饰符(Access Modifiers)
TypeScript 提供三种访问修饰符:
| 修饰符 | 类内 | 子类 | 类外 |
|---|---|---|---|
public | ✅ | ✅ | ✅ |
protected | ✅ | ✅ | ❌ |
private | ✅ | ❌ | ❌ |
class Animal {
public name: string; // 公开
protected species: string; // 受保护
private secret: string; // 私有
constructor(name: string, species: string, secret: string) {
this.name = name;
this.species = species;
this.secret = secret;
}
// 公开方法
public getName(): string {
return this.name;
}
// 受保护方法
protected getSpecies(): string {
return this.species;
}
// 私有方法
private getSecret(): string {
return this.secret;
}
}
class Dog extends Animal {
bark(): string {
console.log(this.name); // ✅ public
console.log(this.species); // ✅ protected
// console.log(this.secret); // ❌ private 不可访问
return "Woof!";
}
}
const dog = new Dog("Buddy", "Canine", "secret");
console.log(dog.name); // ✅ public
// console.log(dog.species); // ❌ protected
// console.log(dog.secret); // ❌ private
ES2022 私有字段(#)
class Account {
#balance: number = 0; // 真正的私有字段(运行时也私有)
deposit(amount: number): void {
this.#balance += amount;
}
getBalance(): number {
return this.#balance;
}
}
const account = new Account();
account.deposit(100);
console.log(account.getBalance()); // 100
// console.log(account.#balance); // ❌ 语法错误
| 特性 | private | # |
|---|---|---|
| 编译时检查 | ✅ | ✅ |
| 运行时私有 | ❌(编译后为普通属性) | ✅ |
| 与 JavaScript 兼容 | ❌ | ✅ |
| 反射访问 | 可以 | 不可以 |
存取器(Accessors)
class Temperature {
private _celsius: number;
constructor(celsius: number) {
this._celsius = celsius;
}
// getter
get fahrenheit(): number {
return this._celsius * 9 / 5 + 32;
}
// setter
set fahrenheit(value: number) {
this._celsius = (value - 32) * 5 / 9;
}
get celsius(): number {
return this._celsius;
}
set celsius(value: number) {
if (value < -273.15) {
throw new Error("温度不能低于绝对零度");
}
this._celsius = value;
}
}
const temp = new Temperature(100);
console.log(temp.fahrenheit); // 212
temp.fahrenheit = 32;
console.log(temp.celsius); // 0
继承(Inheritance)
// 基类
class Shape {
constructor(public color: string) {}
area(): number {
return 0;
}
describe(): string {
return `这是一个 ${this.color} 的图形,面积为 ${this.area()}`;
}
}
// 派生类
class Circle extends Shape {
constructor(color: string, public radius: number) {
super(color); // 调用父类构造函数
}
// 重写方法
override area(): number {
return Math.PI * this.radius ** 2;
}
}
class Rectangle extends Shape {
constructor(
color: string,
public width: number,
public height: number
) {
super(color);
}
override area(): number {
return this.width * this.height;
}
}
const circle = new Circle("红色", 10);
console.log(circle.describe()); // 这是一个 红色 的图形,面积为 314.159...
const rect = new Rectangle("蓝色", 20, 30);
console.log(rect.describe()); // 这是一个 蓝色 的图形,面积为 600
注意:使用
override关键字可以明确标识重写的方法,如果父类没有该方法会报错。
抽象类(Abstract Classes)
抽象类不能被实例化,只能被继承:
abstract class Shape {
constructor(public color: string) {}
// 抽象方法:子类必须实现
abstract area(): number;
abstract perimeter(): number;
// 普通方法:子类可以直接使用
describe(): string {
return `${this.color}图形,面积=${this.area().toFixed(2)}`;
}
}
// ❌ 不能实例化抽象类
// const shape = new Shape("红色"); // 错误
class Circle extends Shape {
constructor(color: string, private radius: number) {
super(color);
}
// 必须实现抽象方法
area(): number {
return Math.PI * this.radius ** 2;
}
perimeter(): number {
return 2 * Math.PI * this.radius;
}
}
class Triangle extends Shape {
constructor(
color: string,
private a: number,
private b: number,
private c: number
) {
super(color);
}
area(): number {
const s = (this.a + this.b + this.c) / 2;
return Math.sqrt(s * (s - this.a) * (s - this.b) * (s - this.c));
}
perimeter(): number {
return this.a + this.b + this.c;
}
}
const shapes: Shape[] = [
new Circle("红", 5),
new Triangle("蓝", 3, 4, 5)
];
shapes.forEach(shape => {
console.log(shape.describe());
});
实现接口(Implements)
interface Serializable {
serialize(): string;
deserialize(data: string): void;
}
interface Printable {
print(): void;
}
// 一个类可以实现多个接口
class Document implements Serializable, Printable {
private content: string = "";
serialize(): string {
return JSON.stringify({ content: this.content });
}
deserialize(data: string): void {
const parsed = JSON.parse(data);
this.content = parsed.content;
}
print(): void {
console.log(this.content);
}
}
静态成员(Static Members)
class MathUtils {
static readonly PI = 3.14159265358979;
static readonly E = 2.71828182845905;
static clamp(value: number, min: number, max: number): number {
return Math.min(Math.max(value, min), max);
}
static lerp(start: number, end: number, t: number): number {
return start + (end - start) * t;
}
}
// 通过类名访问,不需要实例化
console.log(MathUtils.PI);
console.log(MathUtils.clamp(15, 0, 10)); // 10
Mixins(混入)
Mixin 是一种组合模式,允许将多个类的功能组合在一起:
// 基础构造函数类型
type Constructor<T = {}> = new (...args: any[]) => T;
// Mixin 工厂函数
function Timestamped<TBase extends Constructor>(Base: TBase) {
return class extends Base {
createdAt = new Date();
updatedAt = new Date();
touch() {
this.updatedAt = new Date();
}
};
}
function Activatable<TBase extends Constructor>(Base: TBase) {
return class extends Base {
isActive = false;
activate() {
this.isActive = true;
}
deactivate() {
this.isActive = false;
}
};
}
// 基础类
class User {
constructor(public name: string) {}
}
// 组合多个 Mixin
const EnhancedUser = Timestamped(Activatable(User));
const user = new EnhancedUser("Alice");
user.activate(); // 来自 Activatable
user.touch(); // 来自 Timestamped
console.log(user.name); // 来自 User
console.log(user.isActive); // true
console.log(user.createdAt); // Date
类与接口的配合
// 定义接口
interface Repository<T> {
findById(id: number): T | undefined;
findAll(): T[];
save(entity: T): void;
delete(id: number): boolean;
}
// 实现接口
interface User {
id: number;
name: string;
email: string;
}
class InMemoryUserRepository implements Repository<User> {
private users: Map<number, User> = new Map();
private nextId = 1;
findById(id: number): User | undefined {
return this.users.get(id);
}
findAll(): User[] {
return Array.from(this.users.values());
}
save(user: User): void {
if (!user.id) {
user.id = this.nextId++;
}
this.users.set(user.id, user);
}
delete(id: number): boolean {
return this.users.delete(id);
}
}
业务场景:状态机
type OrderStatus = "pending" | "paid" | "shipped" | "delivered" | "cancelled";
interface OrderState {
readonly status: OrderStatus;
pay(): OrderState;
ship(): OrderState;
deliver(): OrderState;
cancel(): OrderState;
}
abstract class BaseOrderState implements OrderState {
constructor(public readonly status: OrderStatus) {}
pay(): OrderState {
throw new Error(`无法从 ${this.status} 状态付款`);
}
ship(): OrderState {
throw new Error(`无法从 ${this.status} 状态发货`);
}
deliver(): OrderState {
throw new Error(`无法从 ${this.status} 状态确认收货`);
}
cancel(): OrderState {
throw new Error(`无法从 ${this.status} 状态取消`);
}
}
class PendingState extends BaseOrderState {
constructor() { super("pending"); }
pay() { return new PaidState(); }
cancel() { return new CancelledState(); }
}
class PaidState extends BaseOrderState {
constructor() { super("paid"); }
ship() { return new ShippedState(); }
cancel() { return new CancelledState(); }
}
class ShippedState extends BaseOrderState {
constructor() { super("shipped"); }
deliver() { return new DeliveredState(); }
}
class DeliveredState extends BaseOrderState {
constructor() { super("delivered"); }
}
class CancelledState extends BaseOrderState {
constructor() { super("cancelled"); }
}
// 使用
let order: OrderState = new PendingState();
order = order.pay(); // pending → paid
order = order.ship(); // paid → shipped
order = order.deliver(); // shipped → delivered
注意事项
- 优先使用
interface而非abstract class来定义契约——接口更灵活 - 使用
override关键字标识重写的方法 - 参数属性可以简化构造函数代码
#私有字段比private更安全(运行时也私有)- Mixin 模式适合需要多重继承的场景