I'm looking to implement a dispatch table in TypeScript where different variations of an entity can have different implementations. I'm getting a TypeScript error that I can't seem to resolve. Here is a minimal repro of the issue:
type AnimalType = 'DOG' | 'CAT';
type Animal<T> = {
type: T;
payload: any;
};
type Hello<T extends AnimalType> = (animal: Animal<T>) => string;
type DispatchTable = {
[T in AnimalType]: Hello<T>;
};
const dispatchTable: DispatchTable = {
DOG: (dog: Animal<'DOG'>) => `"woof!" said ${dog.payload.name}`,
CAT: (cat: Animal<'CAT'>) => `"meow!" said ${cat.payload.name}`,
};
export const hello = <T extends AnimalType>(animal: Animal<T>): string => {
const hi: Hello<T> = dispatchTable[animal.type]; // Type '"CAT"' is not assignable to type '"DOG"'.ts(2322)
return hi(animal);
};
The full error that I'm getting is:
Type 'Hello<"DOG"> | Hello<"CAT">' is not assignable to type 'Hello<T>'.
Type 'Hello<"DOG">' is not assignable to type 'Hello<T>'.
Type 'T' is not assignable to type '"DOG"'.
Type 'AnimalType' is not assignable to type '"DOG"'.
Type '"CAT"' is not assignable to type '"DOG"'.ts(2322)
The type of DispatchTable looks correct, when I hover on it in VSCode I see:
type DispatchTable = {
DOG: Hello<"DOG">;
CAT: Hello<"CAT">;
}
Any ideas what am I doing wrong or is this a limitation of TS? Is there a better pattern to use for implementing different behaviours for different sub types?