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

如何实现TypeScript中针对特定泛型的条件可选回调?

实现仅在数组为number[]时可选回调的sum函数

需求描述

需要实现一个sum函数,接收数组和回调函数,要求仅当数组类型为number[]时,回调函数才是可选的:

  • 传入number[]时,可选择不传回调,直接对数组元素求和;
  • 传入其他类型数组时,必须传入回调函数将元素转为数字后求和。

初始实现及问题

最初的实现代码如下,但存在明显缺陷:

function sum<T>(iter: T[], callback?: (arg: T) => number): number {
    return callback
        ? iter.reduce((sum, element) => sum + callback(element), 0)
        : iter.reduce((sum, x) => sum + Number(x), 0);  // <- 想移除Number()
}

问题点:

  • 代码暗示任意类型T都可以转为数字,不符合实际需求;
  • 当T为number时,会执行冗余的Number()转换。

尝试的重载方案及问题

尝试用重载修复,但TypeScript无法识别"回调为undefined时,T一定是number"的类型关系:

type OptionalIfNumber<T, U> = T extends number ? U | undefined : U;

export function sum(iter: number[], callback?: undefined): number;
export function sum<T>(iter: T[], callback: (arg: T) => number): number;
export function sum<T>(iter: T[], callback: OptionalIfNumber<T, (arg: T) => number>): number {
    return callback
        ? iter.reduce((sum, element) => sum + callback(element), 0)
        : iter.reduce((sum, x) => sum + x, 0);  // <- 此处TypeScript报错
}

可行解决方案

你的需求完全可以实现,核心是利用重载明确区分调用场景,再通过类型断言或类型守卫缩小类型范围:

方案1:结合重载与类型断言

// 重载签名1:number[]时回调可选
export function sum(iter: number[], callback?: undefined): number;
// 重载签名2:非number[]时必须传回调
export function sum<T>(iter: T[], callback: (arg: T) => number): number;
// 实现签名
export function sum<T>(iter: T[], callback?: (arg: T) => number): number {
    if (callback) {
        return iter.reduce((sum, element) => sum + callback(element), 0);
    } else {
        // 重载约束确保此处iter必为number[],安全断言
        return (iter as number[]).reduce((sum, x) => sum + x, 0);
    }
}

解释:重载签名已经明确了两种调用场景,当走到else分支时,必然是传入了number[]且未传回调,因此类型断言是安全的,TypeScript不会再报错。

方案2:结合重载与自定义类型守卫

如果不想用类型断言,可以通过自定义类型守卫让TypeScript自动识别类型:

// 自定义类型守卫:判断是否为number[]
function isNumberArray(arr: unknown): arr is number[] {
    return Array.isArray(arr) && arr.every(item => typeof item === 'number');
}

// 重载签名
export function sum(iter: number[], callback?: undefined): number;
export function sum<T>(iter: T[], callback: (arg: T) => number): number;
// 实现签名
export function sum<T>(iter: T[], callback?: (arg: T) => number): number {
    if (callback) {
        return iter.reduce((sum, element) => sum + callback(element), 0);
    } else if (isNumberArray(iter)) {
        return iter.reduce((sum, x) => sum + x, 0);
    }
    // 重载约束确保不会走到此处,可抛出错误兜底
    throw new Error('参数错误:非数字数组必须传入回调函数');
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.24 18:37:13