TypeScript 类使用完全指南

TypeScript 类使用完全指南

目录

基础概念

类型系统

面向对象特性

高级特性

工程实践

基础概念

基础语法

TypeScript 类的基本语法结构和核心概念。

class Person {
  name: string;
  age: number;

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

  sayHello(): void {
    console.log(`Hello, I'm ${this.name}`);
  }
}

const person = new Person('Alice', 25);
person.sayHello(); // 输出: Hello, I'm Alice

类成员修饰符

TypeScript 提供了多种类成员修饰符,用于控制类成员的行为和特性。

1. static(静态成员)

静态成员属于类本身而不是类的实例,可以通过类名直接访问。

class MathHelper {
  // 静态属性
  static readonly PI = 3.14159;
  private static counter = 0;

  // 静态方法
  static add(x: number, y: number): number {
    return x + y;
  }

  // 静态方法可以访问其他静态成员
  static getNextId(): number {
    return ++MathHelper.counter;
  }
}

2. readonly(只读属性)

readonly 修饰符使属性只能在声明时或构造函数中赋值。

class Configuration {
  readonly apiKey: string;
  static readonly VERSION = '1.0.0';

  constructor(apiKey: string) {
    this.apiKey = apiKey; // 只能在构造函数中赋值
  }
}

3. abstract(抽象成员)

abstract 用于定义抽象类和抽象成员。

abstract class Shape {
  abstract readonly name: string;
  abstract calculateArea(): number;
}

访问修饰符

TypeScript 提供了三种访问修饰符:

1. public(默认)

class PublicExample {
  public name: string;
  age: number; // 隐式 public

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

2. private

class PrivateExample {
  #internalId: string; // ECMAScript 私有字段
  private secretKey: string; // TypeScript private

  constructor(key: string) {
    this.#internalId = `id_${Date.now()}`;
    this.secretKey = key;
  }
}

3. protected

class ProtectedExample {
  protected baseUrl: string;

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

构造函数

构造函数用于初始化类的实例。

class User {
  // 参数属性语法
  constructor(
    public name: string,
    private age: number,
    protected id: string
  ) {}

  // 构造函数重载
  static create(name: string): User;
  static create(config: { name: string; age: number }): User;
  static create(nameOrConfig: string | { name: string; age: number }): User {
    if (typeof nameOrConfig === 'string') {
      return new User(nameOrConfig, 0, 'default');
    } else {
      return new User(nameOrConfig.name, nameOrConfig.age, 'default');
    }
  }
}

属性与方法

类的属性和方法定义了类的数据和行为。

class Product {
  // 属性
  name: string;
  price: number;
  readonly id: string;

  // 可选属性
  description?: string;

  // 方法
  constructor(name: string, price: number) {
    this.name = name;
    this.price = price;
    this.id = Math.random().toString(36).substr(2, 9);
  }

  // 实例方法
  getFormattedPrice(): string {
    return `$${this.price.toFixed(2)}`;
  }

  // 方法重载
  update(name: string): void;
  update(price: number): void;
  update(nameOrPrice: string | number): void {
    if (typeof nameOrPrice === 'string') {
      this.name = nameOrPrice;
    } else {
      this.price = nameOrPrice;
    }
  }
}

访问修饰符

TypeScript 提供了三种访问修饰符来控制类成员的可访问性:

1. public(默认)

  • 如果不指定修饰符,默认就是 public
  • 可以在任何地方被访问(类内部、子类、实例)
  • 适用于需要对外暴露的 API 和公共接口
class PublicExample {
  public name: string; // 显式声明 public
  age: number; // 隐式 public

  constructor(name: string, age: number) {
    this.name = name; // 可以在类内部访问
    this.age = age;
  }
}

const example = new PublicExample('Alice', 25);
console.log(example.name); // 可以在类外部访问
console.log(example.age); // 可以在类外部访问

2. private

  • 只能在声明的类内部访问
  • 子类和实例都不能直接访问
  • 使用 # 符号(ECMAScript 私有字段)或 private 关键字
  • 适用于类的内部实现细节
class PrivateExample {
  #internalId: string; // ECMAScript 私有字段
  private secretKey: string; // TypeScript private

  constructor(key: string) {
    this.#internalId = `id_${Date.now()}`;
    this.secretKey = key;
  }
}

3. protected

  • 可以在声明的类和其子类中访问
  • 实例不能直接访问
  • 适用于需要在继承链中共享的属性和方法
class ProtectedExample {
  protected baseUrl: string;

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

4. 修饰符的组合使用

修饰符可以与其他关键字组合使用:

class ModifierCombination {
  // readonly:只读属性
  public readonly id: string;
  protected readonly createdAt: Date;
  private readonly secret: string;

  // static:静态成员
  public static readonly VERSION = '1.0.0';
  protected static readonly API_KEY = 'xxx';
  private static readonly INTERNAL_CONFIG = {};

  // abstract:抽象成员
  protected abstract readonly type: string;
  protected abstract validate(): boolean;

  constructor(id: string, secret: string) {
    this.id = id; // 只能在构造函数中赋值
    this.createdAt = new Date();
    this.secret = secret;
  }
}

5. 访问修饰符的最佳实践

  1. 封装原则

    • 默认使用 private,除非确实需要对外暴露
    • 通过公共方法控制对私有成员的访问
    • 使用 protected 而不是 public 来共享需要继承的功能
  2. 命名约定

    • 私有成员使用下划线前缀(虽然不是必需的)
    • 使用描述性名称表明可见性意图
  3. 安全性考虑

    • 使用 ECMAScript 私有字段(#)提供运行时私有性保证
    • TypeScript 的 private 只提供编译时检查
    • 敏感数据应该始终使用私有修饰符
  4. 接口设计

    • 公共 API 应该是最小必要集
    • 使用 protected 创建稳定的继承接口
    • 使用私有成员隐藏实现细节
class BestPractices {
  // 私有成员使用下划线前缀
  private _state: string;
  #sensitiveData: string;

  // 受保护的成员用于继承
  protected abstract validateState(state: string): boolean;

  // 公共 API 保持最小化
  public setState(newState: string) {
    if (this.validateState(newState)) {
      this._state = newState;
    }
  }

  // 工厂方法模式示例
  protected abstract createInstance(): BestPractices;
  public static create(): BestPractices {
    const instance = new this();
    return instance.createInstance();
  }
}

构造函数

基本构造函数

class Point {
  constructor(
    public x: number,
    public y: number
  ) {
    // TypeScript 的参数属性语法,自动创建并初始化成员变量
  }
}

多构造函数重载

class DateFormatter {
  constructor(format: string);
  constructor(options: { format: string; timezone: string });
  constructor(formatOrOptions: string | { format: string; timezone: string }) {
    // 实现构造函数
  }
}

属性与方法

只读属性

class Circle {
  readonly PI: number = 3.14159;
  readonly radius: number;

  constructor(radius: number) {
    this.radius = radius; // 只能在构造函数中赋值
  }
}

可选属性

class UserProfile {
  name: string;
  age?: number; // 可选属性
  bio?: string; // 可选属性

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

方法重载

class Calculator {
  add(a: number, b: number): number;
  add(a: string, b: string): string;
  add(a: any, b: any): any {
    return a + b;
  }
}

继承

基本继承

class Animal {
  constructor(protected name: string) {}

  makeSound(): void {
    console.log('Some sound');
  }
}

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

  makeSound(): void {
    console.log('Woof!');
  }

  getInfo(): string {
    return `${this.name} is a ${this.breed}`; // 可以访问 protected name
  }
}

多重继承(通过接口)

interface Flyable {
  fly(): void;
}

interface Swimmable {
  swim(): void;
}

class Duck extends Animal implements Flyable, Swimmable {
  fly(): void {
    console.log('Flying...');
  }

  swim(): void {
    console.log('Swimming...');
  }
}

抽象类

abstract class Shape {
  abstract getArea(): number; // 抽象方法必须在派生类中实现

  printArea(): void {
    // 具体方法可以包含实现
    console.log(`Area: ${this.getArea()}`);
  }
}

class Square extends Shape {
  constructor(private side: number) {
    super();
  }

  getArea(): number {
    return this.side * this.side;
  }
}

接口实现

interface Printable {
  print(): void;
  getContent(): string;
}

class Document implements Printable {
  constructor(private content: string) {}

  print(): void {
    console.log(this.getContent());
  }

  getContent(): string {
    return this.content;
  }
}

静态成员

class MathUtils {
  static readonly PI: number = 3.14159;

  static add(x: number, y: number): number {
    return x + y;
  }

  static {
    // 静态初始化块(TypeScript 4.4+)
    console.log('Class initialized');
  }
}

console.log(MathUtils.PI); // 3.14159
console.log(MathUtils.add(5, 3)); // 8

Getter 和 Setter

TypeScript 中的 getter 和 setter 允许你拦截对类属性的访问和修改,提供了一种控制属性读写的方式。

1. 基本用法

getter 和 setter 可以用来:

  • 验证属性值
  • 转换属性值
  • 计算派生值
  • 触发副作用
  • 实现懒加载
class User {
  private _name: string;
  private _age: number;

  // 基本的 getter 和 setter
  get name(): string {
    return this._name;
  }

  set name(value: string) {
    if (!value) {
      throw new Error('Name cannot be empty');
    }
    this._name = value.trim();
  }

  // 带验证的 setter
  get age(): number {
    return this._age;
  }

  set age(value: number) {
    if (value < 0 || value > 150) {
      throw new Error('Age must be between 0 and 150');
    }
    this._age = value;
  }
}

const user = new User();
user.name = 'John Doe'; // 设置值
console.log(user.name); // 获取值
user.age = 25; // 有效
// user.age = -1;      // 抛出错误

2. 计算属性

使用 getter 实现计算属性,可以根据其他属性动态计算值:

class Circle {
  constructor(private _radius: number) {}

  get radius(): number {
    return this._radius;
  }

  set radius(value: number) {
    if (value < 0) {
      throw new Error('Radius cannot be negative');
    }
    this._radius = value;
  }

  // 计算属性
  get diameter(): number {
    return this._radius * 2;
  }

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

  get circumference(): number {
    return 2 * Math.PI * this._radius;
  }
}

const circle = new Circle(5);
console.log(circle.diameter); // 10
console.log(circle.area); // 78.54...
console.log(circle.circumference); // 31.42...

3. 懒加载

使用 getter 实现属性的懒加载:

class ExpensiveData {
  private _data: number[] | null = null;

  // 懒加载属性
  get data(): number[] {
    if (this._data === null) {
      // 首次访问时才加载数据
      console.log('Loading data...');
      this._data = this.loadData();
    }
    return this._data;
  }

  private loadData(): number[] {
    // 模拟耗时操作
    return Array.from({ length: 1000 }, (_, i) => i);
  }
}

const expensiveData = new ExpensiveData();
// 数据只在首次访问时加载
console.log(expensiveData.data.length); // 输出: Loading data... 1000
console.log(expensiveData.data.length); // 输出: 1000(不会再次加载)

4. 访问控制和验证

使用 getter 和 setter 实现属性访问控制和验证:

class BankAccount {
  private _balance: number = 0;
  private _transactions: number[] = [];

  // 只读属性
  get balance(): number {
    return this._balance;
  }

  // 只读属性,返回副本防止修改
  get transactions(): number[] {
    return [...this._transactions];
  }

  // 验证存款金额
  deposit(amount: number): void {
    if (amount <= 0) {
      throw new Error('Deposit amount must be positive');
    }
    this._balance += amount;
    this._transactions.push(amount);
  }

  // 验证取款金额
  withdraw(amount: number): void {
    if (amount <= 0) {
      throw new Error('Withdrawal amount must be positive');
    }
    if (amount > this._balance) {
      throw new Error('Insufficient funds');
    }
    this._balance -= amount;
    this._transactions.push(-amount);
  }
}

5. 类型转换和格式化

使用 getter 和 setter 实现值的转换和格式化:

class Product {
  private _price: number = 0;
  private _name: string = '';

  get price(): number {
    return this._price;
  }

  set price(value: number) {
    // 保留两位小数
    this._price = Math.round(value * 100) / 100;
  }

  // 格式化价格显示
  get formattedPrice(): string {
    return `$${this._price.toFixed(2)}`;
  }

  get name(): string {
    return this._name;
  }

  set name(value: string) {
    // 转换为标题格式
    this._name = value
      .split(' ')
      .map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
      .join(' ');
  }
}

const product = new Product();
product.price = 19.999;
console.log(product.price); // 20
console.log(product.formattedPrice); // $20.00

product.name = 'gaming LAPTOP';
console.log(product.name); // Gaming Laptop

6. 状态管理和副作用

使用 getter 和 setter 管理状态变化和触发副作用:

type Observer = () => void;

class StateManager {
  private _state: Record<string, any> = {};
  private _observers: Observer[] = [];

  // 代理所有状态访问
  get state(): Readonly<Record<string, any>> {
    return { ...this._state };
  }

  // 设置单个状态
  setState(key: string, value: any): void {
    const oldValue = this._state[key];
    if (oldValue !== value) {
      this._state[key] = value;
      this.notifyObservers();
    }
  }

  // 批量更新状态
  setMultipleStates(updates: Record<string, any>): void {
    let hasChanges = false;
    for (const [key, value] of Object.entries(updates)) {
      if (this._state[key] !== value) {
        this._state[key] = value;
        hasChanges = true;
      }
    }
    if (hasChanges) {
      this.notifyObservers();
    }
  }

  // 观察者模式
  addObserver(observer: Observer): void {
    this._observers.push(observer);
  }

  private notifyObservers(): void {
    this._observers.forEach((observer) => observer());
  }
}

// 使用示例
const stateManager = new StateManager();
stateManager.addObserver(() => console.log('State changed:', stateManager.state));

stateManager.setState('user', { name: 'John' });
stateManager.setMultipleStates({
  theme: 'dark',
  language: 'en',
});

7. 最佳实践

  1. 命名约定:

    • 私有属性使用下划线前缀
    • getter/setter 使用相同的名称(不带下划线)
    • 只读属性可以直接使用 public readonly
  2. 性能考虑:

    • getter 应该是轻量级的
    • 避免在 getter 中进行耗时操作
    • 考虑使用缓存机制
  3. 设计原则:

    • 保持 getter/setter 的简单性
    • 避免在 setter 中触发复杂的副作用
    • 使用 readonly 替代只有 getter 的属性
  4. 错误处理:

    • 在 setter 中进行输入验证
    • 提供清晰的错误信息
    • 考虑使用自定义错误类型
class PropertyError extends Error {
  constructor(
    public propertyName: string,
    public value: any,
    message: string
  ) {
    super(`Invalid value for ${propertyName}: ${message}`);
    this.name = 'PropertyError';
  }
}

class Person {
  private _age: number = 0;

  get age(): number {
    return this._age;
  }

  set age(value: number) {
    if (typeof value !== 'number') {
      throw new PropertyError('age', value, 'Age must be a number');
    }
    if (value < 0 || value > 150) {
      throw new PropertyError('age', value, 'Age must be between 0 and 150');
    }
    this._age = value;
  }
}

装饰器

注意:装饰器是实验性特性,需要在 tsconfig.json 中启用。
// 类装饰器
function logger<T extends { new (...args: any[]): {} }>(constructor: T) {
  return class extends constructor {
    constructor(...args: any[]) {
      super(...args);
      console.log('Instance created');
    }
  };
}

// 方法装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;

  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with:`, args);
    return originalMethod.apply(this, args);
  };

  return descriptor;
}

// 属性装饰器
function required(target: any, propertyKey: string) {
  let value: any;

  const getter = () => value;
  const setter = (newVal: any) => {
    if (newVal === undefined || newVal === null) {
      throw new Error(`${propertyKey} is required`);
    }
    value = newVal;
  };

  Object.defineProperty(target, propertyKey, {
    get: getter,
    set: setter,
    enumerable: true,
    configurable: true,
  });
}

@logger
class Example {
  @required
  name: string;

  @log
  greet(message: string): void {
    console.log(`${this.name} says: ${message}`);
  }
}

高级模式

TypeScript 类支持多种高级设计模式,这些模式可以帮助我们构建更加健壮和可维护的代码。

1. 单例模式(Singleton Pattern)

单例模式确保一个类只有一个实例,并提供对该实例的全局访问点。

使用场景:

  • 全局状态管理
  • 配置管理
  • 数据库连接池
  • 日志记录器
class Singleton {
  private static instance: Singleton;
  private constructor(private config: Record<string, any> = {}) {}

  static getInstance(config?: Record<string, any>): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton(config);
    }
    return Singleton.instance;
  }

  // 线程安全的双重检查锁定模式
  static getInstanceThreadSafe(config?: Record<string, any>): Singleton {
    if (!Singleton.instance) {
      // 在实际场景中,这里应该有适当的锁定机制
      if (!Singleton.instance) {
        Singleton.instance = new Singleton(config);
      }
    }
    return Singleton.instance;
  }

  getConfig(): Record<string, any> {
    return { ...this.config };
  }
}

// 使用示例
const instance1 = Singleton.getInstance({ env: 'dev' });
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

2. 工厂模式(Factory Pattern)

工厂模式提供了创建对象的接口,让子类决定实例化哪个类。

使用场景:

  • 对象创建的封装
  • 依赖注入
  • 平台特定实现
  • 插件系统
// 产品接口
interface Animal {
  makeSound(): void;
  getType(): string;
}

// 具体产品
class Dog implements Animal {
  makeSound(): void {
    console.log('Woof!');
  }

  getType(): string {
    return 'Dog';
  }
}

class Cat implements Animal {
  makeSound(): void {
    console.log('Meow!');
  }

  getType(): string {
    return 'Cat';
  }
}

// 抽象工厂
abstract class AnimalFactory {
  abstract createAnimal(): Animal;

  // 工厂方法
  static createAnimalFactory(type: string): AnimalFactory {
    switch (type.toLowerCase()) {
      case 'dog':
        return new DogFactory();
      case 'cat':
        return new CatFactory();
      default:
        throw new Error(`Unknown animal type: ${type}`);
    }
  }
}

// 具体工厂
class DogFactory extends AnimalFactory {
  createAnimal(): Animal {
    return new Dog();
  }
}

class CatFactory extends AnimalFactory {
  createAnimal(): Animal {
    return new Cat();
  }
}

// 使用示例
const dogFactory = AnimalFactory.createAnimalFactory('dog');
const dog = dogFactory.createAnimal();
dog.makeSound(); // 输出: Woof!

3. Mixin 模式(Mixin Pattern)

Mixin 模式允许将行为注入到类中,实现代码重用和功能组合。

使用场景:

  • 跨类共享行为
  • 实现多重继承
  • 装饰器模式的替代方案
  • 功能模块化
// 定义 Mixin 类型
type Constructor<T = {}> = new (...args: any[]) => T;

// Mixin 函数
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = new Date();

    getTimestamp(): Date {
      return this.timestamp;
    }
  };
}

function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    isActive = false;

    activate(): void {
      this.isActive = true;
    }

    deactivate(): void {
      this.isActive = false;
    }
  };
}

// 基础类
class User {
  constructor(public name: string) {}
}

// 应用 Mixins
const TimestampedUser = Timestamped(User);
const TimestampedActivatableUser = Activatable(TimestampedUser);

// 使用示例
const user = new TimestampedActivatableUser('John');
console.log(user.getTimestamp());
user.activate();
console.log(user.isActive); // true

4. 代理模式(Proxy Pattern)

代理模式为其他对象提供一个代理以控制对这个对象的访问。

使用场景:

  • 延迟加载
  • 访问控制
  • 日志记录
  • 缓存实现
// 目标接口
interface IService {
  getData(): Promise<string>;
}

// 实际服务
class RealService implements IService {
  async getData(): Promise<string> {
    // 模拟耗时操作
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return 'Real service data';
  }
}

// 代理类
class ServiceProxy implements IService {
  private realService: RealService | null = null;
  private cache: string | null = null;

  async getData(): Promise<string> {
    // 缓存检查
    if (this.cache) {
      console.log('Returning cached data');
      return this.cache;
    }

    // 延迟初始化
    if (!this.realService) {
      console.log('Creating real service');
      this.realService = new RealService();
    }

    // 获取数据并缓存
    console.log('Fetching fresh data');
    this.cache = await this.realService.getData();
    return this.cache;
  }

  clearCache(): void {
    this.cache = null;
  }
}

// 使用示例
async function demo() {
  const proxy = new ServiceProxy();
  console.log(await proxy.getData()); // 首次调用,创建服务并获取数据
  console.log(await proxy.getData()); // 使用缓存数据
  proxy.clearCache();
  console.log(await proxy.getData()); // 重新获取数据
}

5. 观察者模式(Observer Pattern)

观察者模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,所有依赖于它的对象都会得到通知。

使用场景:

  • 事件处理系统
  • 状态同步
  • UI 更新
  • 消息推送
interface Observer {
  update(data: any): void;
}

class Subject {
  private observers: Observer[] = [];
  private state: any;

  attach(observer: Observer): void {
    const isExist = this.observers.includes(observer);
    if (!isExist) {
      this.observers.push(observer);
    }
  }

  detach(observer: Observer): void {
    const observerIndex = this.observers.indexOf(observer);
    if (observerIndex !== -1) {
      this.observers.splice(observerIndex, 1);
    }
  }

  notify(): void {
    for (const observer of this.observers) {
      observer.update(this.state);
    }
  }

  setState(state: any): void {
    this.state = state;
    this.notify();
  }
}

// 具体观察者
class ConcreteObserver implements Observer {
  constructor(private name: string) {}

  update(data: any): void {
    console.log(`${this.name} received update with data: ${JSON.stringify(data)}`);
  }
}

// 使用示例
const subject = new Subject();
const observer1 = new ConcreteObserver('Observer 1');
const observer2 = new ConcreteObserver('Observer 2');

subject.attach(observer1);
subject.attach(observer2);
subject.setState({ message: 'Hello!' });

6. 最佳实践和注意事项

  1. 模式选择:

    • 根据实际需求选择合适的模式
    • 避免过度设计
    • 考虑维护成本
    • 注意性能影响
  2. 实现考虑:

    • 保持模式的简单性
    • 提供清晰的文档
    • 考虑错误处理
    • 注意线程安全
  3. 测试策略:

    • 单元测试覆盖
    • 集成测试验证
    • 性能测试
    • 边界情况处理
  4. 性能优化:

    • 延迟初始化
    • 缓存机制
    • 内存管理
    • 避免过度抽象
// 综合示例:结合多个模式的实现
class ApplicationModule {
  private static instance: ApplicationModule;
  private services: Map<string, any> = new Map();
  private observers: Set<Observer> = new Set();

  private constructor() {
    // 私有构造函数
  }

  static getInstance(): ApplicationModule {
    if (!ApplicationModule.instance) {
      ApplicationModule.instance = new ApplicationModule();
    }
    return ApplicationModule.instance;
  }

  // 工厂方法
  createService<T>(type: string, factory: () => T): T {
    if (!this.services.has(type)) {
      const service = factory();
      this.services.set(type, service);
      return service;
    }
    return this.services.get(type);
  }

  // 观察者模式
  subscribe(observer: Observer): void {
    this.observers.add(observer);
  }

  unsubscribe(observer: Observer): void {
    this.observers.delete(observer);
  }

  // 代理模式
  getProxy<T>(target: T): T {
    return new Proxy(target, {
      get: (obj: any, prop: string) => {
        console.log(`Accessing property: ${prop}`);
        return obj[prop];
      },
      set: (obj: any, prop: string, value: any) => {
        console.log(`Setting property: ${prop} = ${value}`);
        obj[prop] = value;
        return true;
      },
    });
  }
}

这些高级模式为我们提供了强大的工具来构建复杂的应用程序。选择合适的模式时,应该考虑实际需求、维护成本和性能影响。同时,也要避免过度设计,保持代码的简单性和可维护性。

类型系统

泛型类

泛型类使得类可以处理多种数据类型,同时保持类型安全。

class Container<T> {
  private value: T;

  constructor(value: T) {
    this.value = value;
  }

  getValue(): T {
    return this.value;
  }

  setValue(value: T): void {
    this.value = value;
  }
}

// 使用示例
const numberContainer = new Container<number>(123);
const stringContainer = new Container<string>('hello');

泛型约束

interface Lengthwise {
  length: number;
}

class LengthChecker<T extends Lengthwise> {
  checkLength(value: T): boolean {
    return value.length > 0;
  }
}

// 使用示例
const checker = new LengthChecker<string>();
console.log(checker.checkLength('test')); // true

类型守卫

类型守卫允许在运行时安全地检查对象的类型。

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

class Dog extends Animal {
  breed: string;
  constructor(name: string, breed: string) {
    super(name);
    this.breed = breed;
  }
}

class Cat extends Animal {
  lives: number;
  constructor(name: string, lives: number = 9) {
    super(name);
    this.lives = lives;
  }
}

class AnimalHandler {
  // instanceof 类型守卫
  static isDog(animal: Animal): animal is Dog {
    return animal instanceof Dog;
  }

  // 自定义类型守卫
  static isCat(animal: Animal): animal is Cat {
    return 'lives' in animal;
  }

  static handleAnimal(animal: Animal) {
    if (AnimalHandler.isDog(animal)) {
      console.log(`${animal.name} is a ${animal.breed} dog`);
    } else if (AnimalHandler.isCat(animal)) {
      console.log(`${animal.name} has ${animal.lives} lives`);
    }
  }
}

索引签名

索引签名允许类动态地添加和访问属性。

class DynamicMap {
  [key: string]: any;

  constructor(initialData: { [key: string]: any } = {}) {
    Object.assign(this, initialData);
  }

  set(key: string, value: any): void {
    this[key] = value;
  }

  get(key: string): any {
    return this[key];
  }
}

// 类型安全的索引签名
class TypedMap<T> {
  [key: string]: T;

  constructor(initialData: { [key: string]: T } = {}) {
    Object.assign(this, initialData);
  }

  set(key: string, value: T): void {
    this[key] = value;
  }

  get(key: string): T | undefined {
    return this[key];
  }
}

面向对象特性

继承

类继承允许创建基于现有类的新类。

class Vehicle {
  constructor(
    protected brand: string,
    protected model: string
  ) {}

  getInfo(): string {
    return `${this.brand} ${this.model}`;
  }
}

class Car extends Vehicle {
  constructor(
    brand: string,
    model: string,
    private doors: number
  ) {
    super(brand, model);
  }

  getInfo(): string {
    return `${super.getInfo()} with ${this.doors} doors`;
  }
}

抽象类

抽象类提供了一个可以被继承但不能被实例化的基类。

abstract class Shape {
  abstract getArea(): number;
  abstract getPerimeter(): number;

  getDescription(): string {
    return `Area: ${this.getArea()}, Perimeter: ${this.getPerimeter()}`;
  }
}

class Rectangle extends Shape {
  constructor(
    private width: number,
    private height: number
  ) {
    super();
  }

  getArea(): number {
    return this.width * this.height;
  }

  getPerimeter(): number {
    return 2 * (this.width + this.height);
  }
}

接口实现

接口定义了类必须实现的契约。

interface Movable {
  move(x: number, y: number): void;
  getPosition(): { x: number; y: number };
}

interface Resizable {
  resize(width: number, height: number): void;
  getSize(): { width: number; height: number };
}

class Sprite implements Movable, Resizable {
  constructor(
    private x: number = 0,
    private y: number = 0,
    private width: number = 0,
    private height: number = 0
  ) {}

  move(x: number, y: number): void {
    this.x = x;
    this.y = y;
  }

  getPosition() {
    return { x: this.x, y: this.y };
  }

  resize(width: number, height: number): void {
    this.width = width;
    this.height = height;
  }

  getSize() {
    return { width: this.width, height: this.height };
  }
}

静态成员

静态成员属于类本身而不是实例。

class Database {
  private static instance: Database;
  private static connections: number = 0;

  private constructor() {}

  static getInstance(): Database {
    if (!Database.instance) {
      Database.instance = new Database();
    }
    return Database.instance;
  }

  static getConnectionCount(): number {
    return Database.connections;
  }

  connect(): void {
    Database.connections++;
  }

  disconnect(): void {
    Database.connections--;
  }
}

Getter 和 Setter

Getter 和 Setter 提供了属性访问的控制。

class Circle {
  private _radius: number = 0;

  get radius(): number {
    return this._radius;
  }

  set radius(value: number) {
    if (value >= 0) {
      this._radius = value;
    } else {
      throw new Error('Radius cannot be negative');
    }
  }

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

  get circumference(): number {
    return 2 * Math.PI * this._radius;
  }
}

高级特性

装饰器

装饰器用于修改类和类成员的行为。

// 类装饰器
function sealed(constructor: Function) {
  Object.seal(constructor);
  Object.seal(constructor.prototype);
}

// 方法装饰器
function log(target: any, propertyKey: string, descriptor: PropertyDescriptor) {
  const originalMethod = descriptor.value;
  descriptor.value = function (...args: any[]) {
    console.log(`Calling ${propertyKey} with:`, args);
    const result = originalMethod.apply(this, args);
    console.log(`Result:`, result);
    return result;
  };
}

@sealed
class Example {
  @log
  multiply(a: number, b: number): number {
    return a * b;
  }
}

装饰器工厂

装饰器工厂允许自定义装饰器的行为。

// 类装饰器工厂
function withVersion(version: string) {
  return function (constructor: Function) {
    constructor.prototype.version = version;
  };
}

// 方法装饰器工厂
function validate(validator: (value: any) => boolean) {
  return function (target: any, propertyKey: string, descriptor: PropertyDescriptor) {
    const originalMethod = descriptor.value;
    descriptor.value = function (...args: any[]) {
      if (args.every(validator)) {
        return originalMethod.apply(this, args);
      }
      throw new Error('Validation failed');
    };
  };
}

@withVersion('1.0.0')
class Calculator {
  @validate((n) => typeof n === 'number' && !isNaN(n))
  add(a: number, b: number): number {
    return a + b;
  }
}

高级模式

TypeScript 类支持多种高级设计模式,这些模式可以帮助我们构建更加健壮和可维护的代码。

1. 单例模式(Singleton Pattern)

单例模式确保一个类只有一个实例,并提供对该实例的全局访问点。

使用场景:

  • 全局状态管理
  • 配置管理
  • 数据库连接池
  • 日志记录器
class Singleton {
  private static instance: Singleton;
  private constructor(private config: Record<string, any> = {}) {}

  static getInstance(config?: Record<string, any>): Singleton {
    if (!Singleton.instance) {
      Singleton.instance = new Singleton(config);
    }
    return Singleton.instance;
  }

  // 线程安全的双重检查锁定模式
  static getInstanceThreadSafe(config?: Record<string, any>): Singleton {
    if (!Singleton.instance) {
      // 在实际场景中,这里应该有适当的锁定机制
      if (!Singleton.instance) {
        Singleton.instance = new Singleton(config);
      }
    }
    return Singleton.instance;
  }

  getConfig(): Record<string, any> {
    return { ...this.config };
  }
}

// 使用示例
const instance1 = Singleton.getInstance({ env: 'dev' });
const instance2 = Singleton.getInstance();
console.log(instance1 === instance2); // true

2. 工厂模式(Factory Pattern)

工厂模式提供了创建对象的接口,让子类决定实例化哪个类。

使用场景:

  • 对象创建的封装
  • 依赖注入
  • 平台特定实现
  • 插件系统
// 产品接口
interface Animal {
  makeSound(): void;
  getType(): string;
}

// 具体产品
class Dog implements Animal {
  makeSound(): void {
    console.log('Woof!');
  }

  getType(): string {
    return 'Dog';
  }
}

class Cat implements Animal {
  makeSound(): void {
    console.log('Meow!');
  }

  getType(): string {
    return 'Cat';
  }
}

// 抽象工厂
abstract class AnimalFactory {
  abstract createAnimal(): Animal;

  // 工厂方法
  static createAnimalFactory(type: string): AnimalFactory {
    switch (type.toLowerCase()) {
      case 'dog':
        return new DogFactory();
      case 'cat':
        return new CatFactory();
      default:
        throw new Error(`Unknown animal type: ${type}`);
    }
  }
}

// 具体工厂
class DogFactory extends AnimalFactory {
  createAnimal(): Animal {
    return new Dog();
  }
}

class CatFactory extends AnimalFactory {
  createAnimal(): Animal {
    return new Cat();
  }
}

// 使用示例
const dogFactory = AnimalFactory.createAnimalFactory('dog');
const dog = dogFactory.createAnimal();
dog.makeSound(); // 输出: Woof!

3. Mixin 模式(Mixin Pattern)

Mixin 模式允许将行为注入到类中,实现代码重用和功能组合。

使用场景:

  • 跨类共享行为
  • 实现多重继承
  • 装饰器模式的替代方案
  • 功能模块化
// 定义 Mixin 类型
type Constructor<T = {}> = new (...args: any[]) => T;

// Mixin 函数
function Timestamped<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    timestamp = new Date();

    getTimestamp(): Date {
      return this.timestamp;
    }
  };
}

function Activatable<TBase extends Constructor>(Base: TBase) {
  return class extends Base {
    isActive = false;

    activate(): void {
      this.isActive = true;
    }

    deactivate(): void {
      this.isActive = false;
    }
  };
}

// 基础类
class User {
  constructor(public name: string) {}
}

// 应用 Mixins
const TimestampedUser = Timestamped(User);
const TimestampedActivatableUser = Activatable(TimestampedUser);

// 使用示例
const user = new TimestampedActivatableUser('John');
console.log(user.getTimestamp());
user.activate();
console.log(user.isActive); // true

4. 代理模式(Proxy Pattern)

代理模式为其他对象提供一个代理以控制对这个对象的访问。

使用场景:

  • 延迟加载
  • 访问控制
  • 日志记录
  • 缓存实现
// 目标接口
interface IService {
  getData(): Promise<string>;
}

// 实际服务
class RealService implements IService {
  async getData(): Promise<string> {
    // 模拟耗时操作
    await new Promise((resolve) => setTimeout(resolve, 1000));
    return 'Real service data';
  }
}

// 代理类
class ServiceProxy implements IService {
  private realService: RealService | null = null;
  private cache: string | null = null;

  async getData(): Promise<string> {
    // 缓存检查
    if (this.cache) {
      console.log('Returning cached data');
      return this.cache;
    }

    // 延迟初始化
    if (!this.realService) {
      console.log('Creating real service');
      this.realService = new RealService();
    }

    // 获取数据并缓存
    console.log('Fetching fresh data');
    this.cache = await this.realService.getData();
    return this.cache;
  }

  clearCache(): void {
    this.cache = null;
  }
}

// 使用示例
async function demo() {
  const proxy = new ServiceProxy();
  console.log(await proxy.getData()); // 首次调用,创建服务并获取数据
  console.log(await proxy.getData()); // 使用缓存数据
  proxy.clearCache();
  console.log(await proxy.getData()); // 重新获取数据
}

5. 观察者模式(Observer Pattern)

观察者模式定义了对象之间的一对多依赖关系,当一个对象改变状态时,所有依赖于它的对象都会得到通知。

使用场景:

  • 事件处理系统
  • 状态同步
  • UI 更新
  • 消息推送
interface Observer {
  update(data: any): void;
}

class Subject {
  private observers: Observer[] = [];
  private state: any;

  attach(observer: Observer): void {
    const isExist = this.observers.includes(observer);
    if (!isExist) {
      this.observers.push(observer);
    }
  }

  detach(observer: Observer): void {
    const observerIndex = this.observers.indexOf(observer);
    if (observerIndex !== -1) {
      this.observers.splice(observerIndex, 1);
    }
  }

  notify(): void {
    for (const observer of this.observers) {
      observer.update(this.state);
    }
  }

  setState(state: any): void {
    this.state = state;
    this.notify();
  }
}

// 具体观察者
class ConcreteObserver implements Observer {
  constructor(private name: string) {}

  update(data: any): void {
    console.log(`${this.name} received update with data: ${JSON.stringify(data)}`);
  }
}

// 使用示例
const subject = new Subject();
const observer1 = new ConcreteObserver('Observer 1');
const observer2 = new ConcreteObserver('Observer 2');

subject.attach(observer1);
subject.attach(observer2);
subject.setState({ message: 'Hello!' });

6. 最佳实践和注意事项

  1. 模式选择:

    • 根据实际需求选择合适的模式
    • 避免过度设计
    • 考虑维护成本
    • 注意性能影响
  2. 实现考虑:

    • 保持模式的简单性
    • 提供清晰的文档
    • 考虑错误处理
    • 注意线程安全
  3. 测试策略:

    • 单元测试覆盖
    • 集成测试验证
    • 性能测试
    • 边界情况处理
  4. 性能优化:

    • 延迟初始化
    • 缓存机制
    • 内存管理
    • 避免过度抽象
// 综合示例:结合多个模式的实现
class ApplicationModule {
  private static instance: ApplicationModule;
  private services: Map<string, any> = new Map();
  private observers: Set<Observer> = new Set();

  private constructor() {
    // 私有构造函数
  }

  static getInstance(): ApplicationModule {
    if (!ApplicationModule.instance) {
      ApplicationModule.instance = new ApplicationModule();
    }
    return ApplicationModule.instance;
  }

  // 工厂方法
  createService<T>(type: string, factory: () => T): T {
    if (!this.services.has(type)) {
      const service = factory();
      this.services.set(type, service);
      return service;
    }
    return this.services.get(type);
  }

  // 观察者模式
  subscribe(observer: Observer): void {
    this.observers.add(observer);
  }

  unsubscribe(observer: Observer): void {
    this.observers.delete(observer);
  }

  // 代理模式
  getProxy<T>(target: T): T {
    return new Proxy(target, {
      get: (obj: any, prop: string) => {
        console.log(`Accessing property: ${prop}`);
        return obj[prop];
      },
      set: (obj: any, prop: string, value: any) => {
        console.log(`Setting property: ${prop} = ${value}`);
        obj[prop] = value;
        return true;
      },
    });
  }
}

这些高级模式为我们提供了强大的工具来构建复杂的应用程序。选择合适的模式时,应该考虑实际需求、维护成本和性能影响。同时,也要避免过度设计,保持代码的简单性和可维护性。

类的模块导入导出

1. 导出最佳实践

// models/user.ts
export interface UserData {
  id: string;
  name: string;
  email: string;
}

export class User {
  constructor(private data: UserData) {}

  static fromJSON(json: string): User {
    return new User(JSON.parse(json));
  }

  toJSON(): string {
    return JSON.stringify(this.data);
  }
}

// 导出类型
export type UserRole = 'admin' | 'user';

// 导出默认值
export default User;

2. 导入最佳实践

// services/user-service.ts
import User, { UserData, UserRole } from '../models/user';

export class UserService {
  private users: Map<string, User> = new Map();

  addUser(data: UserData): void {
    const user = new User(data);
    this.users.set(data.id, user);
  }

  getUser(id: string): User | undefined {
    return this.users.get(id);
  }
}

类的单元测试

1. Jest 测试示例

// user.test.ts
import { User, UserData } from './user';

describe('User', () => {
  let userData: UserData;
  let user: User;

  beforeEach(() => {
    userData = {
      id: '1',
      name: 'John Doe',
      email: 'john@example.com',
    };
    user = new User(userData);
  });

  test('creates user from JSON', () => {
    const json = JSON.stringify(userData);
    const userFromJSON = User.fromJSON(json);
    expect(userFromJSON).toBeInstanceOf(User);
  });

  test('converts to JSON', () => {
    const json = user.toJSON();
    const parsed = JSON.parse(json);
    expect(parsed).toEqual(userData);
  });
});

2. 测试最佳实践

// calculator.test.ts
import { Calculator } from './calculator';

describe('Calculator', () => {
  let calculator: Calculator;

  // 在每个测试前创建新实例
  beforeEach(() => {
    calculator = new Calculator();
  });

  // 分组相关测试
  describe('add', () => {
    test('adds positive numbers', () => {
      expect(calculator.add(2, 3)).toBe(5);
    });

    test('adds negative numbers', () => {
      expect(calculator.add(-2, -3)).toBe(-5);
    });

    test('adds zero', () => {
      expect(calculator.add(2, 0)).toBe(2);
    });
  });

  // 测试异常
  describe('divide', () => {
    test('throws error when dividing by zero', () => {
      expect(() => calculator.divide(1, 0)).toThrow('Division by zero');
    });
  });
});

类的性能优化

1. 内存管理

class ResourceManager {
  private resources: Map<string, WeakRef<Resource>> = new Map();
  private cleanupInterval: number;

  constructor() {
    // 定期清理失效的引用
    this.cleanupInterval = setInterval(() => this.cleanup(), 5000);
  }

  addResource(id: string, resource: Resource): void {
    this.resources.set(id, new WeakRef(resource));
  }

  getResource(id: string): Resource | undefined {
    const ref = this.resources.get(id);
    return ref?.deref();
  }

  private cleanup(): void {
    for (const [id, ref] of this.resources.entries()) {
      if (!ref.deref()) {
        this.resources.delete(id);
      }
    }
  }

  dispose(): void {
    clearInterval(this.cleanupInterval);
    this.resources.clear();
  }
}

2. 性能优化技巧

class OptimizedList<T> {
  private items: T[] = [];
  private cache: Map<string, T> = new Map();

  // 使用缓存优化查找
  find(predicate: (item: T) => boolean): T | undefined {
    const cacheKey = predicate.toString();
    if (this.cache.has(cacheKey)) {
      return this.cache.get(cacheKey);
    }

    const result = this.items.find(predicate);
    if (result) {
      this.cache.set(cacheKey, result);
    }
    return result;
  }

  // 批量操作优化
  addMany(items: T[]): void {
    // 预分配数组空间
    this.items = [...this.items, ...items];
    // 一次性触发更新
    this.notifyUpdate();
  }

  private notifyUpdate(): void {
    // 实现更新通知逻辑
  }
}

类的文档注释

1. JSDoc/TSDoc 规范

/**
 * 表示一个用户实体。
 * 处理用户数据的存储和验证。
 *
 * @class User
 * @implements {IUser}
 * @template T - 用户数据的类型
 *
 * @example
 * ```js
 * const user = new User<UserData>({
 *   id: '1',
 *   name: 'John Doe',
 *   email: 'john@example.com'
 * });

 * ```
 */
class User<T extends UserData> implements IUser<T> {
  /**
   * 创建一个新的用户实例
   *
   * @constructor
   * @param {T} data - 用户数据
   * @throws {ValidationError} 如果数据验证失败
   */
  constructor(private data: T) {
    this.validate();
  }

  /**
   * 验证用户数据
   *
   * @private
   * @returns {void}
   * @throws {ValidationError} 如果验证失败
   */
  private validate(): void {
    if (!this.data.email.includes('@')) {
      throw new ValidationError('Invalid email format');
    }
  }

  /**
   * 更新用户数据
   *
   * @param {Partial<T>} updates - 要更新的字段
   * @returns {void}
   * @emits {UserUpdated} 当用户数据更新时
   */
  update(updates: Partial<T>): void {
    Object.assign(this.data, updates);
    this.validate();
  }
}

2. 文档生成

/**
 * @packageDocumentation
 * 这是用户管理模块的主要文档。
 * 包含了所有用户相关的类和接口。
 */

/**
 * 用户管理器类
 * @remarks
 * 这个类负责处理所有用户相关的操作。
 *
 * @public
 */
export class UserManager {
  /**
   * 用户列表
   * @internal
   */
  private users: Map<string, User> = new Map();

  /**
   * 添加新用户
   * @param user - 要添加的用户对象
   * @returns 添加是否成功
   * @beta
   */
  addUser(user: User): boolean {
    if (this.users.has(user.id)) {
      return false;
    }
    this.users.set(user.id, user);
    return true;
  }

  /**
   * 获取用户信息
   * @param id - 用户ID
   * @returns 用户对象,如果不存在则返回undefined
   * @throws {UserNotFoundError} 当用户不存在时
   */
  getUser(id: string): User {
    const user = this.users.get(id);
    if (!user) {
      throw new UserNotFoundError(id);
    }
    return user;
  }
}

标签: none

添加新评论