typescript fn return map object of composed functions

Viewed 50

i'm trying to type a helper function that takes in an object of functions and wrap it in another fn;

const bindFn = (obj, wrapFn) => Object.entries(obj).reduce((carry, [key, fn])=>{
carry[key] = ( ...args ) => wrapFn(fn.apply(null, args))
return carry;
},{})


// example of usage

const n = bindFn( { sum: (x) => x+1 }, console.log);
n.sum(3); // this should console.log 4

problem is i have no clue how to type bindFn to return proper type that contains an object with same keys and return types of supplied object. something like

interface BindFn = {
[name: keysof obj] : () => Returntype<obj[name]> :D !!! no clue.
}
2 Answers

Let me know if this is what you want? think this is right.

const bindFn = <A extends keyof any, B>(obj: Record<A, B>, wrapFn: (fn: (arg: B) => B) => any): typeof obj => Object.entries(obj).reduce((carry, [key, fn])=>{
    carry[key] = ( ...args: any[] ) => wrapFn(fn.apply(null, args))
    return carry;
}, {})

const obj = { name: () => "my name is", age: () => "my age is" };
const result = bindFn(obj, (arg) => {
    console.log("executed on");
    return arg;
})

or the executed the function, version

const bindFn = <A extends keyof any, B , C>(obj: Record<A, B>, wrapFn: (fn: (arg: B) => any) => C): Record<A, C> => Object.entries(obj).reduce((carry, [key, fn])=>{
    carry[key] = ( ...args: any[] ) => wrapFn(fn.apply(null, args))
    return carry;
}, {})

const obj = { name: () => "my name is", age: () => "my age is" };
const result = bindFn(obj, (arg) => {
    console.log("executed on");
    return 5;
}) // {name: number, age: number}

Try this:

const bindFn = <T extends {[key: string]: Function}>(
  obj: T,
  wrapFn: Function
) => {
  return Object.entries(obj).reduce((carry, [key, fn]) => {
    carry[key] = (...args) => wrapFn(fn.apply(null, args));
    return carry;
  }, {}) as T;
};

const n = bindFn({ sum: (x: number) => x + 1 }, console.log);
n.sum(3);
n.sum('3'); // error: type string does not match number
n.foo(2);   // error: 'foo' does not exist on the first arg of bindFn
const z = bindFn('foo', console.log); // error: 'foo' is not the right shape

This way, you get useful type checking because the return type of bindFn() will always be the same shape as its first parameter. e.g., n.sum() will be of type (x: number) => x + 1 because that's what was passed to bindFn() (because return ... as T)

VSCode hinting

It will also enforce that the shape of the first parameter of bindFn() must be an object with keys that are functions (because T extends {[key: string]: Function})

More VSCode hinting

Related