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

如何使用TypeScript映射ElasticSearch响应并解决TS2339类型报错

ElasticSearch搜索返回值PostSearchResult接口配置及TS2339报错修复

现有项目信息

依赖版本

"@nestjs/elasticsearch": "^8.1.0",
"@elastic/elasticsearch": "^8.2.1",
"@types/elasticsearch": "^5.0.40",

自定义Post相关接口定义

export interface PostSearchResultInterface {
    hits: {
        total: {
            value: number;
        };
        hits: Array<{
            total: {
                value: number;
            }
            _source: PostSearchBodyInterface;
        }>;
    };
}

业务实现代码

const  search  = await this.elasticsearchService.search<PostSearchBodyInterface>({
    index: this.index,
    from: offset,
    size: limit,
    body: {
        query: {
            bool: {
                should: {
                    multi_match: {
                        query: text,
                        fields: ['title', 'story', 'other_titles']
                    }
                },
                filter: {
                    range: {
                        id: {
                            gte: startId
                        }
                    }
                }
            },
        },
        sort: {
            id: {
                order: 'asc'
            }
        }
    }
}) ;

// 此处报错
let count = search.hits.total.value

const hits = search.hits.hits;
const results = hits.map((item) => item._source);
return {
    count : startId ? separateCount : count,
    results
}

报错信息

TS2339类型错误:Property 'value' does not exist on type 'number | SearchTotalHits'. Property 'value' does not exist on type 'number'.
报错位置: let count = search.hits.total.value


报错原因

ElasticSearch 8.x官方客户端的hits.total默认是number | SearchTotalHits联合类型:

  • 当查询配置rest_total_hits_as_int: true时,hits.total直接返回数字格式的总命中数
  • 默认配置下hits.total是对象结构,包含value(总数值)和relation(计数规则)两个字段

当前代码调用search方法时仅传入了文档内容的泛型PostSearchBodyInterface,TS自动使用客户端内置的联合类型做类型校验,直接访问.value属性时,因为联合类型中number类型不存在该属性,就会抛出TS2339错误。
另外自定义的PostSearchResultInterface存在两个问题:一是没有实际传入泛型使用,二是内层hits数组的元素不需要重复定义total字段。


修复方案

三种方案可按需选择:

方案1:类型兼容处理(无需修改查询参数)

对hits.total做类型判断,兼容两种返回格式,不需要额外自定义接口:

const search = await this.elasticsearchService.search<PostSearchBodyInterface>({
  index: this.index,
  from: offset,
  size: limit,
  // 原有查询参数保持不变
  body: {
    query: {
      bool: {
        should: {
          multi_match: {
            query: text,
            fields: ['title', 'story', 'other_titles']
          }
        },
        filter: {
          range: {
            id: {
              gte: startId
            }
          }
        }
      },
    },
    sort: {
      id: {
        order: 'asc'
      }
    }
  }
});

// 兼容两种total类型
const count = typeof search.hits.total === 'number' 
  ? search.hits.total 
  : search.hits.total.value;

const hits = search.hits.hits;
const results = hits.map((item) => item._source);
return {
  count : startId ? separateCount : count,
  results
}

方案2:配置参数让total直接返回数字

在查询参数中添加rest_total_hits_as_int: true,此时hits.total会直接返回数字类型,不需要访问.value属性:

const search = await this.elasticsearchService.search<PostSearchBodyInterface>({
  index: this.index,
  from: offset,
  size: limit,
  rest_total_hits_as_int: true, // 新增该配置
  body: {
    // 原有查询体保持不变
  }
});

// 直接取值即可,不需要.value
const count = search.hits.total;

const hits = search.hits.hits;
const results = hits.map((item) => item._source);
return {
  count : startId ? separateCount : count,
  results
}

方案3:使用自定义接口作为返回类型

先修正接口定义错误,再把接口作为第二个泛型参数传入search方法:
首先修正接口:

interface PostSearchResultInterface {
  hits: {
    total: {
      value: number;
      relation?: string;
    };
    hits: Array<{
      _source: PostSearchBodyInterface;
    }>;
  };
}

调用时指定返回类型:

// 第二个泛型参数传入自定义的返回结构类型
const search = await this.elasticsearchService.search<PostSearchBodyInterface, PostSearchResultInterface>({
  // 查询参数不变
});
// 此时TS会识别total为带value的对象类型,不会报错
const count = search.hits.total.value;

注意:该方案需要确认ES查询配置下hits.total确实返回对象格式,否则运行时会出现取值错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 06:30:50