What I want
I have an object and a 'consumer' function that takes a key and handler function, then passes the value for that key to the handler.
I want Typescript to infer the correct type for the handler function, given an arbitrary key at runtime.
I want this to use the key as a 'selector' for the correct handler, and have type safety in using that handler. I know I could manually type the argument in the handler given the key, but I want it to be inferred automatically to have proper type safety. For example, to break if I change the key and not the implementation of the handler.
What I've tried
type Things<T> = {
[K in keyof T]: T[K]
}
type ThingConsumer<T, K extends keyof T> = (
forThingOfType: K,
handler: (value: T[K]) => void,
) => void
function createThings<T>(things: Things<T>): Things<T> {
return things
}
function createThingConsumer<T, K extends keyof T>(
things: Things<T>,
): ThingConsumer<T, K> {
return (forThingOfType, handler) => handler(things[forThingOfType])
}
const things = createThings({
a: { foo: 1, bar: 2 },
b: { baz: 3 },
})
const thingConsumer= createThingConsumer(things)
thingConsumer('a', (value) => {
console.log(value.foo + 1) // <-- error: Object may be 'undefined'
})
What doesn't work
It appears that Typescript can't infer the concrete type of value correctly as { foo: number, bar: number } from the passed key, 'a'.
Instead, it infers value as a union type of all possible values of things, i.e.
{ foo: number, bar: number } | { baz: number }
And then the error message when trying to use value.foo is very unexpected. It says that the "Object" may be undefined. If anything, I would have expected it to complain that foo might be undefined, given the union type inference, but not that value itself may be undefined.
Is what I'm trying to do possible?
It feels like it ought to be - in particular I really thought that the T[K] syntax says 'for a given concrete key K of an object, resolve the type of the corresponding value'. But perhaps it's not that powerful and is instead saying 'for any key, resolve any value'?