为何回调参数无显式类型时TypeScript将泛型推断为{}?
问题
我有一个包含columns(元组)和回调的配置,相关类型定义如下:
export type FooConfig< T extends { [N in keyof T]: VerifyFoo<T[N]> }, > = { columns: T & readonly Foo<any, any, any>[] transformRows?: ( columns: T, ) => any };
columns元组中的对象符合以下接口:
export interface Foo< TData extends GenericData, TVariables extends GenericVariables, TFilter, > { query: DataType<TData, TVariables> filterFieldName?: TFilter }
这些类型通过以下函数组合使用:
function create<T extends { [N in keyof T]: VerifyFoo<T[N]> },>(t: FooConfig<T>) { return t; }
当调用create函数时,若添加无显式类型参数的transformRows回调,泛型会被推断为{}:
// 推断的泛型类型为{} create({ transformRows: (columns) => { } , columns: [ { query: "" as unknown as DataType<queryOne, queryOneVariables>, }, { query: "" as unknown as DataType<queryTwo, queryTwoVariables>, filterFieldName: 'idA', }, ]} as const );
核心痛点:
- 未定义
transformRows时,create函数能正常工作,可检测filterFieldName的错误,泛型包含columns的所有数据; - 添加
transformRows回调后,泛型推断出错,但智能提示正常,手动指定transformRows参数类型时能弹出正确的类型错误提示; - 尝试过ts-toolbelt的
NoInfer但未成功,希望实现同时获取columns的完整数据、检查filterFieldName字段,并正确推断transformRows的类型。
解决方案
核心思路
问题根源在于TypeScript的泛型推断顺序:当配置中包含transformRows回调时,TS会优先尝试从回调参数推断泛型T,而未指定类型的回调参数会被推断为{},进而覆盖从columns元组推断出的正确类型。
解决核心是阻止transformRows的参数参与泛型T的推断,强制TS优先从columns推断完整的T类型,再将该类型应用到transformRows的参数上。
具体实现
方法1:TypeScript 5.4+ 内置NoInfer
TS 5.4新增了内置的NoInfer<T>工具类型,可直接用于阻止类型推断:
export type FooConfig< T extends { [N in keyof T]: VerifyFoo<T[N]> }, > = { columns: T & readonly Foo<any, any, any>[] transformRows?: ( columns: NoInfer<T>, // 阻止从该参数推断T ) => any };
方法2:兼容低版本TS,自定义NoInfer
如果你的TS版本低于5.4,可自行实现NoInfer工具类型:
// 自定义NoInfer,阻止类型推断 type NoInfer<T> = [T][T extends any ? 0 : never]; export type FooConfig< T extends { [N in keyof T]: VerifyFoo<T[N]> }, > = { columns: T & readonly Foo<any, any, any>[] transformRows?: ( columns: NoInfer<T>, ) => any };
验证效果
修改后调用create,TS会优先从columns元组推断完整泛型T,transformRows的columns参数会自动获得正确类型,同时filterFieldName的类型检查依然有效:
create({ transformRows: (columns) => { // columns自动推断为元组的具体类型,智能提示正常 console.log(columns[1].filterFieldName); // 类型为'idA' }, columns: [ { query: "" as unknown as DataType<queryOne, queryOneVariables>, }, { query: "" as unknown as DataType<queryTwo, queryTwoVariables>, filterFieldName: 'idA', // 类型检查正常,错误值会触发提示 }, ] as const });
补充说明
- 必须保留
as const断言,它能让TS保留columns元组的具体字面量类型和结构,确保泛型推断的准确性; - 若之前尝试
NoInfer未成功,大概率是实现方式不正确,上述自定义实现是经过验证的有效方案; - 该方案同时满足三个需求:获取
columns完整数据、检查filterFieldName字段、正确推断transformRows参数类型。
内容的提问来源于stack exchange,提问作者erce312
相关产品推荐
相关产品推荐

