I’m trying to build a dynamic dispatch, similar to this:
enum AnimalType {
Rabbit = 'Rabbit',
Duck = 'Duck',
}
interface RabbitInput {
animalType: AnimalType.Rabbit;
food: string;
}
interface DuckInput {
animalType: AnimalType.Duck;
swim_speed: number;
}
type AnimalInput = RabbitInput | DuckInput
type AnimalFunctionMap = {
[AnimalType.Duck]: (input: DuckInput) => Promise<any>;
[AnimalType.Rabbit]: (input: RabbitInput) => Promise<any>;
};
const eat = async (input: RabbitInput) => {
return { 'status': 'satisfied' };
}
const swim = async(input: DuckInput) => {
return { 'status': 'fast' };
}
const animalFunctions: AnimalFunctionMap = {
[AnimalType.Duck]: swim,
[AnimalType.Rabbit]: eat,
};
function isRabbitInput(input: AnimalInput): input is RabbitInput {
return input.animalType === AnimalType.Rabbit
}
function isDuckInput(input: AnimalInput): input is DuckInput {
return input.animalType === AnimalType.Duck
}
const applyAnimalBehaviour = async (input: AnimalInput): Promise<any> => {
const animalFunction = animalFunctions[input.animalType];
if (animalFunction) {
if (isRabbitInput(input)) {
return await animalFunction(input);
} else if (isDuckInput(input)) {
return await animalFunction(input);
} else {
throw new Error(`Unsupported animal type.`);
}
} else {
throw new Error(`No animal function found for type.`);
}
};
For reasons I don’t understand, this yields:
Argument of type 'DuckInput' is not assignable to parameter of type 'never'.
The intersection 'DuckInput & RabbitInput' was reduced to 'never' because property 'animalType' has conflicting types in some constituents.(2345)
I would have thought that I could get TS to infer that the types of the applyAnimalBehaviour parameters would be matched to an appropriate function, even without the type guards. But I can’t make it work.