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

如何根据第一个参数的类型定义回调函数的签名?

如何根据第一个参数的类型定义回调函数的签名?

看起来你遇到的问题是泛型约束在处理对象类型时,没办法正确推断出回调里值的类型对吧?我来帮你分析下问题所在,然后给出修复方案。

问题根源

你原来的each函数泛型定义里,V是一个独立的泛型参数,当传入Record<string, Date>这类对象时,TypeScript没办法把V和对象的属性值类型(也就是Date)正确关联起来——因为T extends Record<K, V>这个约束比较松散,V可以被推断为任意类型,最终就变成了unknown。

修复方案

我们可以直接从传入的data类型T中推导回调参数的类型,不需要依赖独立的V、K泛型参数。这里有两种简洁的实现方式:

方式一:使用条件类型的泛型函数

直接通过条件类型分别处理数组和对象的情况,从T中提取值和键的类型:

declare const each: <T extends Array<any> | Record<PropertyKey, any>>(
    data: T,
    callback: T extends Array<infer V> 
        ? (d: V, k: number) => void 
        : (d: T[keyof T], k: keyof T) => void
) => void;

方式二:使用函数重载

这种方式更直观,分别为数组和对象场景定义重载签名:

// 数组类型的重载
declare function each<V>(data: V[], callback: (d: V, k: number) => void): void;
// 对象类型的重载
declare function each<T extends Record<PropertyKey, any>>(data: T, callback: (d: T[keyof T], k: keyof T) => void): void;

验证效果

修复后再运行你的示例代码,两种场景都能正确推断类型了:

function bar(arr: Array<Set<number>>, obj: Record<string, Date>) {
    each(arr, (v, k) => {
        // ✅ v 是 Set<number>, k 是 number
        v.add(1); // 可以正常调用 Set 的方法
    });

    each(obj, (v, k) => {
        // ✅ v 是 Date, k 是 string
        console.log(v.getFullYear()); // 可以正常调用 Date 的方法
    });
}

核心思路

不管用哪种方式,核心都是让回调函数的参数类型直接依赖于第一个参数data的类型T,而不是用独立的泛型参数间接关联,这样TypeScript就能准确完成类型推断啦。

备注:内容来源于stack exchange,提问作者Guile

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 09:19:33