升级至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
相关产品推荐
相关产品推荐

