如何编写适配继承顶级接口的通用TypeScript持久化函数
解决方案:实现通用的持久化动物类型转换函数
我们需要调整类型定义逻辑,让每个动物类型能明确区分未持久化和持久化状态,同时建立两者的关联,确保函数能精准完成类型转换。
1. 重构基础类型与动物类型
将动物接口改为泛型结构,通过persisted参数控制是否包含id属性,同时继承对应的基础接口:
interface UnpersistedAnimal { name: string; } interface PersistedAnimal extends UnpersistedAnimal { id: number; } // 泛型基础类型,通过persisted参数切换状态 type AbstractAnimal<T extends boolean = true> = T extends true ? PersistedAnimal : UnpersistedAnimal; // 泛型Duck类型,继承对应状态的AbstractAnimal interface Duck<T extends boolean = false> extends AbstractAnimal<T> { name: "Duck"; } // 泛型Goose类型,同理 interface Goose<T extends boolean = false> extends AbstractAnimal<T> { name: "Goose"; }
现在可以直接用Duck<false>表示未持久化鸭子,Duck<true>表示持久化鸭子,TypeScript会自动校验属性合法性。
2. 实现通用的persistAnimal函数
定义类型工具统一转换逻辑,让函数接收任意未持久化动物类型,返回对应的持久化版本:
// 类型工具:将未持久化类型转换为持久化类型 type ToPersisted<T> = T extends AbstractAnimal<false> ? T & PersistedAnimal : never; function persistAnimal<T extends AbstractAnimal<false>>(animal: T): ToPersisted<T> { const persisted = animal as ToPersisted<T>; persisted.id = Math.floor(Math.random() * 1000); // 模拟生成唯一ID return persisted; }
3. 使用验证
测试代码完全符合预期:
// 未持久化鸭子,无id属性 const unpersistedDuck: Duck<false> = { name: "Duck" }; console.log(unpersistedDuck.id); // TypeScript提示错误:属性'id'不存在于类型'Duck<false>'上 // 持久化后得到带id的Duck类型 const persistedDuck = persistAnimal(unpersistedDuck); console.log(persistedDuck.id); // 正常,类型为number console.log(persistedDuck.name); // 类型保持为"Duck" // Goose类型同理 const unpersistedGoose: Goose<false> = { name: "Goose" }; console.log(unpersistedGoose.id); // 错误提示 const persistedGoose = persistAnimal(unpersistedGoose); console.log(persistedGoose.id); // 正常访问
关键优化点
- 让动物类型本身成为泛型,明确关联两种状态,避免交叉类型的模糊性
- 用类型工具
ToPersisted统一转换逻辑,保证类型推断准确 - 函数内部类型断言更精准,避免不必要的类型拓宽
内容的提问来源于stack exchange,提问作者tobias47n9e
相关产品推荐
相关产品推荐

