You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript类序列化与持久化:如何实现DRY及类型安全?

TypeScript持久化的DRY实现与类型安全优化

一、简化Storable类的重复代码

抽象基类+泛型方案

创建通用抽象基类封装构造、序列化/反序列化逻辑,子类仅需定义自身属性,无需重复编写通用代码:

interface Storable {
  id: string;
  serialize(): string;
  deserialize(data: string): void;
}

abstract class BaseStorable<T extends BaseStorable<T>> implements Storable {
  id: string;

  constructor(data?: Partial<T>) {
    data && Object.assign(this, data);
  }

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

  deserialize(data: string): void {
    Object.assign(this, JSON.parse(data));
  }

  // 静态方法:直接从序列化字符串生成实例
  static fromSerialized<T extends BaseStorable<T>>(this: new () => T, data: string): T {
    const instance = new this();
    instance.deserialize(data);
    return instance;
  }
}

// 子类示例:仅定义属性+可选自定义构造逻辑
class User extends BaseStorable<User> {
  name: string;
  age: number;

  constructor(data?: Partial<User>) {
    super(data);
    // 自定义默认值或初始化逻辑
    this.age = this.age ?? 18;
  }
}

如果需要自定义序列化/反序列化逻辑,子类直接重写对应方法即可,核心通用逻辑仍复用基类。

装饰器方案(可选)

若需要更灵活的扩展,可使用装饰器自动生成通用方法(需启用TypeScript的experimentalDecorators编译选项):

function StorableDecorator<T extends { new(...args: any[]): {} }>(constructor: T) {
  return class extends constructor implements Storable {
    id: string;

    constructor(...args: any[]) {
      super(...args);
      const [data] = args;
      data && Object.assign(this, data);
    }

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

    deserialize(data: string): void {
      Object.assign(this, JSON.parse(data));
    }
  };
}

// 使用装饰器
@StorableDecorator
class Product {
  id: string;
  price: number;
}

二、Store接口的类型安全优化

单类型绑定Store

通过泛型约束让Store与具体Storable类型绑定,实现读写时的自动类型校验与推导:

interface Store<T extends Storable> {
  writeItem(item: T): Promise<void>;
  readItem(id: string): Promise<T | null>;
}

class TypedLocalStore<T extends BaseStorable<T>> implements Store<T> {
  private readonly ctor: new () => T;

  constructor(ctor: new () => T) {
    this.ctor = ctor;
  }

  async writeItem(item: T): Promise<void> {
    localStorage.setItem(item.id, item.serialize());
  }

  async readItem(id: string): Promise<T | null> {
    const data = localStorage.getItem(id);
    return data ? this.ctor.fromSerialized(data) : null;
  }
}

// 使用示例
const userStore = new TypedLocalStore(User);
const user = await userStore.readItem("user_123");
// user 自动推导为 User | null
await userStore.writeItem(new User({ id: "user_123", name: "Alice" }));
// 传入非User类型会直接触发编译错误

多类型映射Store

如果需要一个Store处理多种Storable类型,通过类型映射定义键与类型的对应关系,实现精准的类型约束:

// 定义存储类型映射
type StoreTypeMap = {
  user: User;
  product: Product;
};

interface MultiTypeStore {
  writeItem<K extends keyof StoreTypeMap>(key: K, item: StoreTypeMap[K]): Promise<void>;
  readItem<K extends keyof StoreTypeMap>(key: K, id: string): Promise<StoreTypeMap[K] | null>;
}

class LocalMultiStore implements MultiTypeStore {
  private readonly ctors: { [K in keyof StoreTypeMap]: new () => StoreTypeMap[K] } = {
    user: User,
    product: Product,
  };

  async writeItem<K extends keyof StoreTypeMap>(key: K, item: StoreTypeMap[K]): Promise<void> {
    localStorage.setItem(`${key}_${item.id}`, item.serialize());
  }

  async readItem<K extends keyof StoreTypeMap>(key: K, id: string): Promise<StoreTypeMap[K] | null> {
    const data = localStorage.getItem(`${key}_${id}`);
    return data ? this.ctors[key].fromSerialized(data) : null;
  }
}

// 使用示例
const multiStore = new LocalMultiStore();
await multiStore.writeItem("user", new User({ id: "user_456", name: "Bob" }));
const product = await multiStore.readItem("product", "prod_789");
// product 自动推导为 Product | null
// 传入未定义的key(如"order")会直接触发编译错误

内容的提问来源于stack exchange,提问作者Alan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.19 06:22:04