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

升级至TypeScript 4.8时遇到的类型交集兼容性问题

从TypeScript 4.5升级到4.8时的pg类型兼容问题

定义的类型与接口

我封装了@types/pg的相关类型,定义如下:

export interface IQueryConfig {
    text: string;
    values?: any[];
}

export interface IQueryResult<T> {
    rows: T[];
    rowCount: number;
}

export interface IDbClient {
    query<TResult = any>(queryConfig: IQueryConfig | string): Promise<IQueryResult<TResult>>;
}

问题代码与报错

最初的实现代码在TS4.5中正常,但升级到4.8后触发类型错误:

export const dbClient: IDbClient = {
    query: async q => await pool.query(q)
}

报错信息:

Type 'Promise<QueryResult<QueryResultRow>>' is not assignable to type 'Promise<IQueryResult<T>>'.
  Type 'QueryResult<QueryResultRow>' is not assignable to type 'IQueryResult<T>'.
    Types of property 'rows' are incompatible.
      Type 'QueryResultRow[]' is not assignable to type 'T[]'.
        Type 'QueryResultRow' is not assignable to type 'T'.
          'T' could be instantiated with an arbitrary type which could be unrelated to 'QueryResultRow'.ts(2322)

可行的修改写法

将代码调整为以下形式后,类型检查通过:

export const dbClient: IDbClient = {
    query: async q => {
        const result = await client.query(q);
        return result;
    }
}

pg的参考类型定义

export interface QueryResultBase {
    command: string;
    rowCount: number;
    oid: number;
    fields: FieldDef[];
}

export interface QueryResultRow {
    [column: string]: any;
}

export interface QueryResult<R extends QueryResultRow = any> extends QueryResultBase {
    rows: R[];
}

原因分析

TS4.8引入了更严格的类型收窄机制:

  • 直接返回await pool.query(q)时,TypeScript会直接推断返回类型为Promise<QueryResult<QueryResultRow>>,而IDbClient的query方法要求返回Promise<IQueryResult<TResult>>——其中TResult是调用方可指定的任意泛型类型,QueryResultRow无法保证与任意TResult兼容。
  • 增加中间变量result后,TypeScript会触发结构兼容性检查:由于QueryResult包含IQueryResult要求的所有属性(rows和rowCount),因此可以被兼容到IQueryResult<T>。

解决方案

方案1:保留中间变量的写法

利用TypeScript的结构兼容性完成类型推断,这是最直观的解决方式:

export const dbClient: IDbClient = {
    query: async q => {
        const result = await pool.query(q);
        return result;
    }
}

方案2:显式类型断言

通过类型断言告知TypeScript返回值符合IQueryResult<TResult>的要求:

export const dbClient: IDbClient = {
    query: async <TResult = any>(q: IQueryConfig | string) => 
        await pool.query(q) as unknown as IQueryResult<TResult>
}

方案3:对齐类型定义

让自定义的IQueryResult继承pg的QueryResultBase,使两者类型结构完全匹配,这样QueryResult<T>会天然兼容IQueryResult<T>:

import { QueryResultBase } from 'pg';

export interface IQueryResult<T> extends QueryResultBase {
    rows: T[];
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 21:51:16