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

如何为含复杂对象结构的TypeScript函数调整泛型类型?

问题描述

现有如下TypeScript代码片段:

type Query = {
  model: string;
  on: string;
  fields: string[];
  query?: Query[];
};

function getModel(config: {
  object: xmlrpc.Client
  db: string
  userId: number
  password: string
}) {
  return <T extends { [key: string]: any }>(
    model: string,
    fields: Extract<keyof T, string>[],
    props?: {
      offset?: number
      limit?: number
      where?: [string, string, any][]
      query?: Query[]
    }
  ): Promise<(T & { id: number })[]> =>
    new Promise((resolve, reject) => {
      // Implementation details
    }).then(async (res) => {
      // Post-processing logic
    });
}

调用getModel时,result的推断类型不符合预期,调用代码如下:

const config = { object, db, userId, password };

const result = await getModel(config)(
  "product.template",
  ["name", "categ_id", "attribute_line_ids", "service_type"],
  {
    limit: 5,
    where: [["categ_id", "=", 40]],
    query: [
      {
        model: "product.template.attribute.line",
        on: "attribute_line_ids",
        fields: ["value_ids"],
        query: [
          {
            model: "product.attribute.value",
            on: "value_ids",
            fields: ["name"],
          },
        ],
      },
    ],
  }
);

期望result的类型为:

const result: ({
  name: any
  categ_id: any
  attribute_line_ids: ({ value_ids: ({ name: any } & { id: number })[] } & {
    id: number
  })[]
  service_type: any
} & { id: number })[]

如何调整泛型类型定义,确保推断类型与预期结构匹配?


解决方案

要让TypeScript正确推断嵌套关联的类型,需要让类型系统能递归识别查询的层级结构,具体调整如下:

1. 重构Query为递归泛型类型

将原有的Query类型改为带泛型参数的递归类型,明确字段和嵌套查询的类型关联:

type Query<Fields extends string = string, NestedQueries extends Query<any, any>[] = Query<any, any>[]> = {
  model: string;
  on: string;
  fields: Fields[];
  query?: NestedQueries;
};

2. 定义递归解析结果的工具类型

创建ResolveQueryResult工具类型,用来递归解析每个查询对应的返回结构:

type ResolveQueryResult<Q extends Query<any, any>> = 
  Q extends Query<infer Fields, infer NestedQueries>
    ? (Record<Fields, any> & { id: number }) & {
        [K in NestedQueries[number]["on"]]: ResolveQueryResult<Extract<NestedQueries[number], { on: K }>>[]
      }
    : never;

这个类型会:

  • 生成当前查询字段的键值对类型,同时附加必有的id字段
  • 遍历所有嵌套查询,将on指定的字段映射为对应嵌套查询的结果数组

3. 调整getModel的泛型参数

修改getModel的返回函数,让它接收顶层字段和嵌套查询作为泛型参数,组合出最终的返回类型:

function getModel(config: {
  object: xmlrpc.Client
  db: string
  userId: number
  password: string
}) {
  return <TopLevelFields extends string, NestedQueries extends Query<any, any>[] = []>(
    model: string,
    fields: TopLevelFields[],
    props?: {
      offset?: number
      limit?: number
      where?: [string, string, any][]
      query?: NestedQueries
    }
  ): Promise<(
    Record<TopLevelFields, any> & 
    { id: number } & 
    { [K in NestedQueries[number]["on"]]: ResolveQueryResult<Extract<NestedQueries[number], { on: K }>>[] }
  )[]> =>
    new Promise((resolve, reject) => {
      // Implementation details
    }).then(async (res) => {
      // Post-processing logic
    });
}

验证效果

现在调用getModel时,TypeScript会自动推断出与你预期完全一致的result类型:

const result = await getModel(config)(
  "product.template",
  ["name", "categ_id", "attribute_line_ids", "service_type"],
  {
    limit: 5,
    where: [["categ_id", "=", 40]],
    query: [
      {
        model: "product.template.attribute.line",
        on: "attribute_line_ids",
        fields: ["value_ids"],
        query: [
          {
            model: "product.attribute.value",
            on: "value_ids",
            fields: ["name"],
          },
        ],
      },
    ],
  }
);

此时result的推断类型为:

({
  name: any;
  categ_id: any;
  attribute_line_ids: ({
    value_ids: ({
      name: any;
    } & { id: number; })[];
  } & { id: number; })[];
  service_type: any;
} & { id: number; })[]

扩展说明

如果需要替换any为更具体的字段类型,可以给Query和ResolveQueryResult添加额外泛型参数传入具体类型定义,当前实现已满足结构推断的核心需求。


内容的提问来源于stack exchange,提问作者stu

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 20:56:06