Home > front end >  TypeScript: Why is this return value unknown when the callback function is typed?
TypeScript: Why is this return value unknown when the callback function is typed?

Time:03-07

I'm trying to figure out how to type this fmap function I have. Here's my latest attempt:

export const __skip__ = Symbol('skip');

type ReduceFn<T> = Array<T>['reduce']

/**
 * Filter-map. Like map, but you may omit entries by returning `__skip__`.
 */
export function fmap<TVal,TReturn>(this:TVal[], callback: (this: TVal[], ...args:[currentValue: TVal, index: number])=>TReturn|typeof __skip__): TReturn[] {
    return Array.prototype.reduce.call(this, (accum: TReturn[], ...args) => {
        let x = callback.call(this, ...args);
        if (x !== __skip__) {
            accum.push(x);
        }
        return accum;
    }, []);
}

enter image description here

Even though it knows what callback returns:

enter image description here

How come? How do I fix the errors?

N.B. I typed args as [currentValue: TVal, index: number] to try to simplify a bit, but I think it should be Parameters<ReduceFn<TVal>>


Also, I know the signature is a bit funny. The usage of this is intentional for legacy reasons.

Here's an example usage:

const keys = fmap.call(feeIds, id => feeIdToKey[id] || __skip__)

CodePudding user response:

Seems like the method Array.prototype.reduce.call is trying to infer the type automatically based on the generic type of Array, but is unable to infer it properly since the default generic type of Array is any (that is Array<any>). But you can pass in whichever generic type you want to the method call, manually, rather than relying on the automatic inference in this specific instance.

Here's how you can do it:

export function fmap<TVal, TReturn>(this: TVal[], callback: (this: TVal[], ...args: [currentValue: TVal, index: number, array: TVal[]]) => TReturn | typeof __skip__): TReturn[] {
    return Array.prototype.reduce.call<
        // Here's our manually passed in generic type
        TVal[], [callbackfn: (previousValue: TReturn[], currentValue: TVal, currentIndex: number, array: TVal[]) => TReturn[], initialValue: TReturn[]], TReturn[]
    >(this, (accum: TReturn[], ...args) => {
        let x = callback.call(this, ...args);
        if (x !== __skip__) {
            accum.push(x);
        }
        return accum;
    }, []);
};

Playground link

NOTE

I also had to add the parameter array: TVal[] to this line: callback: (this: TVal[], ...args: [currentValue: TVal, index: number, array: TVal[]]) => TReturn | typeof __skip__, since Array.prototype.reduce's callback param expects also a fourth, array, parameter.

  • Related