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
相关产品推荐
相关产品推荐

