实现带可选累加器的类型化reduce函数,如何避免类型断言及解析类型报错?
我希望实现一个带有可选累加器(accumulator)参数的类型化reduce函数,目前已通过类型断言实现,但想避免使用类型断言。相关代码如下:
type IF_UNDEFINED<A, T> = A extends undefined ? T : A; export function reduce<T, A>( values: T[], callback: (prev: A, current: T) => A, accumulator: A ): A; export function reduce<T>( values: T[], callback: (prev: T, current: T) => T, accumulator?: undefined ): T; export function reduce<T, A>( values: T[], callback: (value1: IF_UNDEFINED<A, T>, value2: T) => IF_UNDEFINED<A, T>, accumulator?: A ): IF_UNDEFINED<A, T> { let currentValue = ( accumulator !== undefined ? accumulator : values[0] ) as IF_UNDEFINED<A, T>; let nextValue: T; for (let i = 1; i < values.length; i++) { nextValue = values[i]; currentValue = callback(currentValue, nextValue); } return currentValue; }
如果移除currentValue的类型断言,TypeScript会报错:Type 'T | (A & ({} | null))' is not assignable to type 'IF_UNDEFINED<A, T>'。我搞不懂为什么TypeScript推断出的类型是(A & ({} | null))而非A,求解答。
类型推断异常的原因
TypeScript的类型收缩逻辑对泛型参数存在局限:当你用accumulator !== undefined判断时,它只能确定accumulator不是undefined,但无法确认泛型A是否包含null或其他 falsy 值。为了保证类型安全,它会将accumulator的类型收窄为A & ({} | null)——这个交集类型表示A排除了undefined,但保留了null和所有非空对象类型({}匹配除null/undefined外的非原始类型)。
而你的IF_UNDEFINED<A, T>逻辑是A extends undefined ? T : A,TypeScript无法自动将A & ({} | null)与IF_UNDEFINED<A, T>划等号,因此抛出类型不兼容的错误。
避免类型断言的解决方案
方案1:拆分条件分支明确类型
将赋值和循环逻辑拆分为两个独立分支,让TypeScript在每个分支中都能精准推断类型:
type IF_UNDEFINED<A, T> = A extends undefined ? T : A; export function reduce<T, A>( values: T[], callback: (prev: A, current: T) => A, accumulator: A ): A; export function reduce<T>( values: T[], callback: (prev: T, current: T) => T, accumulator?: undefined ): T; export function reduce<T, A>( values: T[], callback: (value1: IF_UNDEFINED<A, T>, value2: T) => IF_UNDEFINED<A, T>, accumulator?: A ): IF_UNDEFINED<A, T> { let currentValue: IF_UNDEFINED<A, T>; if (accumulator !== undefined) { currentValue = accumulator; // 此处TypeScript确认类型为A,符合IF_UNDEFINED<A,T> for (const val of values) { currentValue = callback(currentValue, val); } } else { currentValue = values[0]; // 此处类型为T,符合IF_UNDEFINED<A,T>(此时A为undefined) for (let i = 1; i < values.length; i++) { currentValue = callback(currentValue, values[i]); } } return currentValue; }
方案2:给泛型添加约束
通过泛型约束排除A为undefined的可能,让TypeScript更准确地推断类型:
type IF_UNDEFINED<A, T> = A extends undefined ? T : A; export function reduce<T, A>( values: T[], callback: (prev: A, current: T) => A, accumulator: A ): A; export function reduce<T>( values: T[], callback: (prev: T, current: T) => T, accumulator?: undefined ): T; export function reduce<T, A = never>( values: T[], callback: (value1: IF_UNDEFINED<A, T>, value2: T) => IF_UNDEFINED<A, T>, accumulator?: A ): IF_UNDEFINED<A, T> { let currentValue: IF_UNDEFINED<A, T> = accumulator !== undefined ? accumulator : values[0]!; // 非空断言匹配原代码未处理空数组的逻辑 for (let i = accumulator !== undefined ? 0 : 1; i < values.length; i++) { currentValue = callback(currentValue, values[i]); } return currentValue; }
内容的提问来源于stack exchange,提问作者rainerhahnekamp

