0

The following function workOnFunctions takes an object and an array of keys to that object where all the keys map to a function:

const myObj = {
    a: () => 1,
    b: "test",
    c: 2,
    d: (a : number) => a*2
}

function workOnFunctions <K extends PropertyKey>(o: Record<K, Function>, names: K[]) {
    names.forEach(name => {
        const f =  o[name]; // do something with function f
    })
}

I noticed that it works as intended when I do this:

workOnFunctions(myObj, ["a", "d"]); // works

But when storing the array in a variable and then passing this variable to the function, I get the error

Argument of type '{ a: () => number; b: string; c: number; d: (a: number) => number; }' is not assignable to parameter of type 'Record<string, Function>'.
  Property 'b' is incompatible with index signature.
    Type 'string' is not assignable to type 'Function'.

Code:

const names = ["a", "d"];
workOnFunctions(myObj, names);

Why is that the case? It seems like the compiler can't infer the type of names correctly. When I pass the array directly, it can. I also tried this:

const names = ["a", "d"] as const;
workOnFunctions(myObj, names);

Which does not work either.

My final goal is to write a function that takes an object and an array of keys that point to functions within that object.

EDIT: Partial solution found, but I don't understand why

Looks like this works:

type KeysMatching<T, V> = { [K in keyof T]: T[K] extends V ? K : never }[keyof T]
const names : Array<KeysMatching<typeof myObj, Function>> = ["a", "d"];
workOnFunctions(myObj, names);

I got KeysMatching from here: TypeScript: Accept all Object keys that map to a specific type

But why can the compiler infer this complex type by itself when I directly pass the array? Is there a trick to auto-infer the type also when using a separate array?

1 Answer 1

0

Your error is not because of the names array, but because of your myObject, the "b" and "c" properties does not return a function, which is what you have defined in your workOnFunctions function o: Record<K, Function>.

If you change your myObj with the correct types, it will work

// adding the type :Record<PropertyKey, Function> is optional here, and will work without having to defined the type.
myObj: Record<PropertyKey, Function> = {
    a: () => 1,
    b: () => "test",
    c: () => 2,
    d: (a : number) => a*2
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.