如何实现TypeScript RecordLoader自动推导add指定的返回字段类型?
如何让TypeScript数据库Loader的add方法自动推导返回的属性类型
我想实现一个TypeScript数据库库,让开发者通过add方法指定要返回的记录属性,最终load()的返回类型能精确包含这些被添加的键。
给定的记录类型定义
type GenericRecord = { [key: string]: string | number }; type UserRecord = { id: string; name: string; age: number; };
期望的使用方式
开发者可以链式调用add方法指定属性,最终返回类型自动匹配选中的键:
const userLoader = new RecordLoader<UserRecord>(); const user = userLoader.add("name").add("age").load(); // 期望user的类型为 {name: string, age: number}
我的尝试与问题
我最初的实现无法动态生成包含具体键名的返回类型:
class RecordLoader<FullRecord extends GenericRecord, PartialRecord extends Partial<FullRecord> = {}> { private attrsToLoad: (keyof FullRecord)[] = []; constructor(attrsToLoad: (keyof FullRecord)[] = []) { this.attrsToLoad = attrsToLoad; } add(attrName: keyof FullRecord) { this.attrsToLoad.push(attrName); // 核心问题:没法用attrName变量生成包含具体键名的交叉类型 return new RecordLoader<FullRecord, PartialRecord & { [attrName: string]: string }>(); } load() { return loadData<PartialRecord>(this.attrsToLoad); } } function loadData<PartialRecord extends GenericRecord>(keys: (keyof PartialRecord)[]) { // 模拟数据加载逻辑 return {} as PartialRecord; }
虽然可以把add改成泛型方法,但会导致冗余——既要手动指定泛型类型,又要传字符串参数:
add<T extends Partial<FullRecord>>(attrName: keyof FullRecord){ this.attrsToLoad.push(attrName); return new RecordLoader<FullRecord, PartialRecord & T>(); } // 使用时必须重复写类型,冗余繁琐 const userLoader = new RecordLoader<UserRecord>(); const user = userLoader.add<{name: string}>("name").add<{age: number}>("age").load();
解决方案:自动推导选中的键类型
通过给add方法添加泛型参数捕获具体键名,再从FullRecord中提取对应类型,就能实现无冗余的自动推导:
class RecordLoader<FullRecord extends GenericRecord, Selected extends keyof FullRecord = never> { private attrsToLoad: Selected[] = [] as Selected[]; constructor(attrsToLoad: Selected[] = [] as Selected[]) { this.attrsToLoad = attrsToLoad; } add<K extends keyof FullRecord>(attrName: K) { this.attrsToLoad.push(attrName as Selected & K); // 将新选中的键合并到已选集合中 return new RecordLoader<FullRecord, Selected | K>([...this.attrsToLoad, attrName] as (Selected | K)[]); } load() { // 从FullRecord中提取已选键对应的完整类型 type Result = Pick<FullRecord, Selected>; return loadData<Result>(this.attrsToLoad as (keyof Result)[]); } } function loadData<Result extends GenericRecord>(keys: (keyof Result)[]) { // 模拟实际数据库数据加载逻辑 const data: Partial<Result> = {}; keys.forEach(key => { // 这里仅为示例,实际应从数据库获取对应值 data[key] = "mock_data" as Result[keyof Result]; }); return data as Result; }
使用效果
现在无需手动指定泛型,TypeScript会自动推导返回类型:
const userLoader = new RecordLoader<UserRecord>(); const user = userLoader.add("name").add("age").load(); // user的类型自动推导为 { name: string; age: number; }
内容的提问来源于stack exchange,提问作者Nate
相关产品推荐
相关产品推荐

