You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何实现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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 20:05:32