How to make “handler” lookup for “dispatcher” style function be typed correctly

Yeah, there’s no convenient and type-safe solution for you here. I’ve opened an issue at microsoft/TypeScript#30581 about this but I don’t expect it to be addressed.

I see two main ways forward. One is to just use a type assertion, since you legitimately know more than the compiler does here. It could be like this:

function handleInfo(info: TInfo) {
    // assert your way out.  Not type safe but convenient!
    (handlers[info.valueType] as (x: number | string)=>any)(info.value); 
}

Now there’s no error. It’s not type safe. But it’s convenient and doesn’t change the emitted JavaScript.


Or you could try to walk the compiler through the cases and prove to it that all is fine. This is complex, brittle, and has runtime effects:

const typeGuards: {
  [P in keyof TInfoTypeMap]: (x: TInfoTypeMap[keyof TInfoTypeMap])=>x is TInfoTypeMap[P];
} = {
    num: (x:any): x is number => typeof x === "number",
    str: (x:any): x is string => typeof x === "string"
}

function narrowTInfo<K extends keyof TAllPossibleTInfoMap>(
  x: TInfo, v: K): x is TAllPossibleTInfoMap[K] {
    return typeGuards[v](x.value);
} 

function handleInfo(info: TInfo) {
    if (narrowTInfo(info, "num")) {
        handlers[info.valueType](info.value); // okay
    } else {
        handlers[info.valueType](info.value); // okay
    }
}

That works but is obnoxious. So I’d recommend an assertion.

Hope that helps; good luck!

Leave a Comment