TypeScript已类型标注的回调返回值为unknown的原因及修复方案
问题原因
变量x被推断为unknown的核心原因是TypeScript对重载原型方法的call调用推断能力有限,且你在reduce回调中使用了无类型标注的...args剩余参数:
Array.prototype.reduce是拥有多重重载的方法,直接通过.call调用时,TS无法自动根据你传入的回调反推参数类型- 无标注的
...args默认被推断为类型不明确的元组,TS无法确认其是否匹配callback的入参要求,因此会将callback的调用返回值保守推断为unknown - 额外注意:你定义的
ReduceFn<T>直接取Array<T>['reduce']的类型,由于TS对重载方法取Parameters时只会返回最后一个重载的参数列表,拿到的类型和你实际使用的「带初始值的reduce」参数并不匹配。
修复方案
不需要绕弯用Array.prototype.reduce.call,在fmap函数内部this已经被标注为TVal[]类型,直接调用实例的reduce方法即可让TS完成正确的类型推断,同时显式标注回调参数避免隐式any:
export const __skip__ = Symbol('skip'); /** * Filter-map. Like map, but you may omit entries by returning `__skip__`. */ export function fmap<TVal, TReturn>( this: TVal[], callback: (currentValue: TVal, index: number, array: TVal[]) => TReturn | typeof __skip__ ): TReturn[] { return this.reduce((accum: TReturn[], currentValue, index, array) => { // x会被正确推断为 TReturn | typeof __skip__ const x = callback(currentValue, index, array); if (x !== __skip__) { accum.push(x); } return accum; }, []); }
如果你出于某些原因必须保留Array.prototype.reduce.call的写法,需要显式给.call传入泛型参数明确类型,避免TS的保守推断:
export function fmap<TVal,TReturn>(this:TVal[], callback: (currentValue: TVal, index: number, array: TVal[])=>TReturn|typeof __skip__): TReturn[] { return Array.prototype.reduce.call< TVal[], [callback: (accum: TReturn[], currentValue: TVal, index: number, array: TVal[]) => TReturn[], initialValue: TReturn[]], TReturn[] >(this, (accum, currentValue, index) => { const x = callback(currentValue, index, this); if (x !== __skip__) { accum.push(x); } return accum; }, []); }
验证
修复后你的典型调用场景可以获得完全正确的类型推断:
const feeIds = [1,2,3] const feeIdToKey: Record<number, string> = {1: 'a', 3: 'c'} // keys被正确推断为string[],值为['a','c'] const keys = fmap.call(feeIds, id => feeIdToKey[id] || __skip__)
内容的提问来源于stack exchange,提问作者mpen
相关产品推荐
相关产品推荐

