Search code examples
typescriptreact-nativeenumsunion

Parameter of union type of enums?


What I have are two enums

enum Fruit { Apples, Oranges };
enum Vegetable { Garlic, Cucumber };

and a type

type Food = Fruit | Vegetable;

Now I want to make another type that is a function and takes a food as a parameter

type OnViewFood = (food: Food) => void;

But when I do

viewFruit: OnViewFood = (fruit: Fruit) => `This is ${fruit}`;

I get an error that Food is not assignable to Fruit. How do I achieve my goal?


Solution

  • The reason the compiler doesn't want you to do this is because it is essentially not safe. Consider if this example were valid:

    // we assign a function that can only handle Fruit.
    let viewFruit: OnViewFood = (fruit: Fruit) => `This is ${fruit}`; 
    
    // the declared signature allows us to use a Vegetable, 
    // possibly causing an error
    viewFruit(Vegetable.Cucumber);  
    

    You can declare OnViewFood to be a union of functions, but then calling becomes an issue since you can't call a union of functions and you can't easily type-guard :

    type OnViewFood = ((food: Fruit) => void) | ((food: Vegetable) => void);
    let viewFruit: OnViewFood = (fruit: Fruit) => `This is ${fruit}`;
    
    // ok now can be either function signature 
    viewFruit(Vegetable.Cucumber); // now an error 
    

    In either case we can use a type assertion to get around the type error, and you can go for either option, depending on which side you trust more, the function creator side or the caller side:

    // Type assertion on call 
    type OnViewFood = ((food: Fruit) => void) | ((food: Vegetable) => void);
    let viewFruit: OnViewFood = (fruit: Fruit) => `This is ${fruit}`;
    
    (viewFruit as any as ((food: Vegetable) => void))(Vegetable.Cucumber);
    
    // Type assertion on declaration 
    type OnViewFood = (food: Food) => void
    let viewFruit: OnViewFood = ((fruit: Fruit) => `This is ${fruit}`) as (food: Food) => void;
    viewFruit(Vegetable.Cucumber);