1

Is there a way to make a generic T extend either type1 or type2 in TypeScript?

For example, I have the following:

interface Edible { }

interface Fruit extends Edible {
    isFruity: boolean
}
interface Veggie extends Edible {
    isHealthy: boolean;
}
interface Dairy extends Edible {
    isHighProtein: boolean;
}

interface Apple extends Fruit {
    nice: boolean;
    sweet: boolean;
}
// and so on so forth...

Now I want to have a function that takes in something that's either a Fruit or Veggie, how do I make it happen? Is this the right way to express "extend either Fruit or Veggie" at all?

function yum<T extends Fruit | Veggie>(thing: T) {
    //...
    if ("isFruity" in thing) {
        thing.isFruity;  // Error: Property 'isFruity' does not exist on type 'T'.
    }
}

Further more, how to narrow down the type even further to Apple, for example?

function yum<T extends Fruit | Veggie>(thing: T) {
    //...
    if ("isFruity" in thing) {
        thing.isFruity;  // Error: Property 'isFruity' does not exist on type 'T'.

        if ("sweet" in thing) {
            console.log("this is an apple!");
            thing.sweet; // Error: Property 'sweet' does not exist on type 'T'.
        }
    }
}
diyeb
  • 11
  • 2
  • If you trust "isFruity" means it's a fruit, you can always cast `(thing as Fruit).isFruity`, though a better way to identify an object is with the "is" syntax (see "is Animal" at https://medium.com/ovrsea/checking-the-type-of-an-object-in-typescript-the-type-guards-24d98d9119b0). – user2740650 Jan 11 '21 at 22:52
  • This is probably a better example of the "is" syntax: https://stackoverflow.com/questions/40081332/what-does-the-is-keyword-do-in-typescript – user2740650 Jan 11 '21 at 22:53
  • @user2740650 As I understand, `is` is a type predicate so can't be used to test the type in code itself. Or what do you mean exactly? – diyeb Jan 11 '21 at 22:56
  • I think `as` can definitely get it to work, but is there a better way preferably not involving type casting? – diyeb Jan 11 '21 at 22:57
  • 1
    I meant to write a function like `function isFruit(test: any): test is Fruit {return !!test.isFruity}` to have a single function that can identify a Fruit. If you call that, TypeScript is smart enough to know anything inside `if (isFruit() {....}` is truly a Fruit so you don't need an explicit cast. – user2740650 Jan 11 '21 at 22:58
  • I'll try that, thanks – diyeb Jan 11 '21 at 23:03

0 Answers0