TypeScript如何通过泛型重载类内方法实现Redis键值类型约束
问题描述
是否存在通用实现方式,可在类构造阶段传入所有键的规则、以及每个键对应操作接收/返回的数据类型,自动完成类内相关方法的类型约束?
目前通过手动编写方法重载类型的方式实现该效果,代码如下:
class Redis { get() { } set() { } // ... 其余大量方法 } const redis = new Redis(); type IEmailKey = `unverified-email:${number}`; type IEmailData = string; type IDidRemindKey = `did-remind:${number}`; type IDidRemindData = boolean; type IStrictGet = { (key: IEmailKey): Promise<IEmailData | null>; (key: IDidRemindKey): Promise<IDidRemindData | null>; }; type IRedisSetParams = Parameters<Redis['set']>; type IStrictSet = { ( key: IEmailKey, value: IEmailData, options?: IRedisSetParams[2] ): Promise<'OK' | null>; ( key: IDidRemindKey, value: IDidRemindData, options?: IRedisSetParams[2] ): Promise<'OK' | null>; }; // 该类型包含所有键的定义,以及每个键对应存储的数据类型 type IStrictRedis = Omit<Redis, 'get' | 'set'> & { get: IStrictGet; set: IStrictSet; };
上述方案可实现精准的类型校验:调用redis.get('unverified-email:123')、redis.set('unverified-email:123', 'foo@example.com')、redis.get('did-remind:123')、redis.set('did-remind:123', true)时,TypeScript可自动校验键与对应值的类型匹配关系。
希望找到更简便的实现方案,直接给class Redis传入泛型即可自动完成上述方法类型重载逻辑,预期使用方式的伪代码如下:
const redis = new Redis<{ [`unverified-email:${number}`]: string; [`did-remind:${number}`]: boolean; }>();
实现方案
直接通过泛型+映射类型自动推导方法类型即可,不需要手动编写每组键值的重载签名,完整实现代码如下:
// 保留原始Redis类的基础实现,内部业务逻辑不需要改动 class BaseRedis { get(key: string): Promise<any> { // 原有get业务逻辑 return Promise.resolve(null) } set(key: string, value: any, options?: any): Promise<'OK' | null> { // 原有set业务逻辑 return Promise.resolve('OK') } // 其余原有业务方法保持不变 } // 定义支持泛型的Redis类,接收键到存储值类型的映射作为泛型参数 class Redis<TKeyMap extends Record<string, any>> extends BaseRedis { get<K extends keyof TKeyMap>(key: K): Promise<TKeyMap[K] | null> { return super.get(key as string) } set<K extends keyof TKeyMap>( key: K, value: TKeyMap[K], options?: Parameters<BaseRedis['set']>[2] ): Promise<'OK' | null> { return super.set(key as string, value, options) } }
使用方式和预期完全一致:
const redis = new Redis<{ [`unverified-email:${number}`]: string; [`did-remind:${number}`]: boolean; }>(); // 以下调用会自动触发类型校验 await redis.get('unverified-email:123') // 推导返回类型为 Promise<string | null> redis.set('unverified-email:123', 'foo@example.com') // 校验通过 redis.set('unverified-email:123', true) // 类型报错,值类型不匹配 await redis.get('did-remind:123') // 推导返回类型为 Promise<boolean | null> redis.set('did-remind:123', true) // 校验通过 redis.set('did-remind:123', 'test') // 类型报错,值类型不匹配
该方案不需要手动维护方法重载列表,新增键规则时只需要在传入的泛型类型中追加对应的键值映射即可,类型校验逻辑会自动生效。
相比手动编写重载的方案,核心优势如下:
- 代码量大幅降低,不需要为每组键重复写get/set的重载签名
- 类型规则集中维护,新增、修改键映射时不需要调整类本身的类型定义
- 原生兼容模板字面量类型的模糊匹配规则,校验精度和手动重载完全一致
如果需要支持更复杂的键匹配规则,只需要在泛型参数的推导逻辑中补充对应的条件类型判断即可,基础版本已经覆盖绝大多数业务场景。
内容的提问来源于stack exchange,提问作者Noitidart
相关产品推荐
相关产品推荐

