Reputation: 5007
SO, I have a function, that takes values of object and return another object that use those values as keys. It looks something like this:
function fn(actions) {
return keys.reduce((acc, m) => {
const name = actions[m]
acc[name] = () => { /* some function */}
return acc
}, {})
}
Now, somehow I want to type it so that when I call it
res = fn({"something": "go", "something else": "run"})
res. // ...
autocomplete would work to suggest that there are indeed 2 methods can be invoked "go" and "run".
So basically I expect res to have type of { [key in 'go' | 'run']: Function }
.
Is that possible to achieve with TS?
Upvotes: 1
Views: 932
Reputation: 250396
You need to capture the string literal types in the object. You could use as const
on the object itself, or you can use the trick that TS will preserve the string literal types if the string literal is assigned to a generic type parameter.
With the string literal types you can then create the mapped type you need:
function fn<V extends string, T extends Record<keyof T, V>>(actions: T) {
var keys = Object.keys(actions) as Array<keyof T>
return keys.reduce((acc, m) => {
const name = actions[m]
acc[name] = () => { /* some function */}
return acc
}, {} as { [P in T[keyof T]]: () => void })
}
let res = fn({"something": "go", "something else": "run"})
res.go()
res.run();
res.Run() // err
Upvotes: 1