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

为何回调参数无显式类型时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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 17:42:32