如何用TypeScript编写可复用的数据库Wrapper适配旧数据库?
问题描述
接手了无法重构的劣质数据库,需要用TypeScript编写中间层Wrapper,实现GetObjectWithID、Delete等基础操作。已为各数据库对象创建Interface并存储于interfaces.ts,希望通过Wrapper避免大量复制粘贴代码。
需求要点:
- 用静态类实现Wrapper,无需实例化,仅用于封装对应实体的数据库调用
- 静态实体类(如
User)的方法要返回对应Interface类型的对象 - 复杂场景下需要转换数据库返回的原始对象,使其适配定义好的接口
当前错误示例代码:
import UserInterface from 'interfaces.ts'; import db from 'the_database'; class DatabaseObject { private static objectClass: string; private constructor() {} static getObject(id: string): object { return db.fetch(this.objectClass, id) } } class User extends DatabaseObject { private static objectClass = User; getObject(id: string): UserInterface { return super.getObject(id); // 此处明显错误 } }
期望的调用方式:
import UserInterface from 'interfaces.ts'; import User from 'db_wrapper.ts'; const myUserId: string = "abcd1234"; const myUser: UserInterface = User.getObject(myUserId); console.log(myUser.username); console.log(myUser.email);
解决方案
可以通过泛型基类实现复用逻辑,同时让子类指定对应的接口类型和数据库标识,具体实现如下:
1. 基类实现(封装通用逻辑)
import db from 'the_database'; // 泛型基类,T代表当前实体对应的接口类型 abstract class DatabaseObject<T> { // 子类需重写该静态属性,指定数据库中对应的对象标识(如表名/类名) protected static objectKey: string; // 私有构造函数,禁止实例化 private constructor() {} // 通用的获取对象方法,返回指定接口类型T static getObject<T>(this: typeof DatabaseObject<T>, id: string): T { // 调用数据库获取原始数据 const rawData = db.fetch(this.objectKey, id); // 调用子类的转换方法,将原始数据转为接口类型对象 return this.transform(rawData); } // 抽象转换方法,子类必须实现,用于适配数据库原始数据到接口 protected static abstract transform<T>(rawData: unknown): T; }
2. 子类实现(User实体)
import { DatabaseObject } from './DatabaseObject'; import { UserInterface } from './interfaces'; class User extends DatabaseObject<UserInterface> { // 指定数据库中User对应的标识 protected static objectKey = 'User'; // 实现转换逻辑,适配数据库返回的原始数据到UserInterface protected static transform(rawData: unknown): UserInterface { // 这里根据实际数据库返回结构做转换,比如字段映射、类型转换 const raw = rawData as Record<string, unknown>; return { id: raw.id as string, username: raw.user_name as string, // 假设数据库字段是user_name,接口是username email: raw.email as string, // 其他字段的转换逻辑 }; } }
3. 调用验证
此时可以按照期望的方式调用,TypeScript会自动推导返回类型为UserInterface:
import { User } from './db_wrapper'; const myUserId = "abcd1234"; const myUser = User.getObject(myUserId); // 类型自动推导为UserInterface console.log(myUser.username); console.log(myUser.email);
关键说明
- 泛型
T确保基类方法能返回子类指定的接口类型,避免类型断言 objectKey静态属性由子类指定,替代原错误示例中直接赋值类的逻辑transform抽象方法强制子类实现数据转换逻辑,适配劣质数据库的字段差异- 基类的
getObject方法通过this: typeof DatabaseObject<T>约束静态方法的this类型,确保子类调用时能正确关联泛型参数
内容的提问来源于stack exchange,提问作者Graytr
相关产品推荐
相关产品推荐

