如何为TypeScript泛型约束实现`+`运算符,优化sumBy支持bigint
优化
sumBy函数以支持number和bigint类型 先给出一个简洁且类型安全的实现,解决你当前的冗余判断和空数组返回类型问题:
type Numeric = number | bigint; type Zero<T extends Numeric> = T extends number ? 0 : 0n; export function sumBy<T, U extends Numeric>( array: T[], mapperFn: (item: T) => U ): U { // 处理空数组,返回对应类型的初始值 if (array.length === 0) { return (Zero<U> as unknown) as U; } return array.reduce((sum, currentItem) => { const currentValue = mapperFn(currentItem); // 类型守卫确保同类型相加,避免number与bigint混用 if (typeof sum === "number" && typeof currentValue === "number") { return sum + currentValue as U; } if (typeof sum === "bigint" && typeof currentValue === "bigint") { return sum + currentValue as U; } // 理论上不会执行到这里,因为mapperFn返回类型为U,保证类型统一 throw new Error("Invalid numeric type combination"); }, mapperFn(array[0]) as U); }
解答你的两个核心疑问
1. 为什么无法直接告知TypeScript这两种类型都实现了+运算符?
TypeScript的类型系统中不存在“可加法运算”的通用约束,原因有两点:
number和bigint是语言内置的原始类型,它们的+支持是硬编码在语言逻辑中的,没有对应的接口或抽象类型可以复用;- 这两种类型的加法是互斥的——
number + bigint会直接触发类型错误,TS无法为它们定义一个共享的“可加”约束,必须显式区分处理。
2. 为什么不能用U is number or bigint这样的泛型签名?
U is number | bigint是类型谓词的语法,只能用于函数返回值,作用是在运行时缩小变量的类型范围,不能用来定义泛型约束。泛型约束必须使用U extends number | bigint(也就是你最初使用的写法)。
要让TS确保输入输出类型一致,关键是:
- 用数组第一个元素的映射值作为累加初始值,保证累加器类型与
U完全匹配; - 通过类型守卫在运行时验证累加器和当前值的类型,避免跨类型相加;
- 为空数组返回对应类型的初始值(
0或0n),消除返回类型中的undefined。
内容的提问来源于stack exchange,提问作者Augustin Riedinger
相关产品推荐
相关产品推荐

