5

So here's my problem, let's say I have some types of animals:

interface IAnimal {
   type: string;
   legs: number;
}

interface IChicken extends IAnimal {
   eggsPerDay: number;
}

interface ICow extends IAnimal {
   milkProduction: number;
}

...

doSomethingWithAnimal(animal: IChicken|ICow): void {
    switch(animal.type){
        case 'cow':
            alert(`the milk production is ${animal.milkProduction}`);
    }
}

The problem that I have is typescript doesn't know if it's a cow or a chicken. Is there a way that once I know for sure that the animal is of type ICow that I can safely access those additional attributes?

I know that it's possible to do const cow = animal as ICow but wouldn't that needlessly copy my variable? The example that I provided is an oversimplification since the problem that I'm actually trying to solve has ten derived classes and the function is called a ton, so I want to keep it as efficient as possible.

0

2 Answers 2

3

You can use a discriminated union for this task. You just need to add a type member to each memebr of the union with the actual type as a string literal type:

interface IAnimal {
  type: string;
  legs: number;
}

interface IChicken extends IAnimal {
  type: "chicken";
  eggsPerDay: number;
}

interface ICow extends IAnimal {
  type: "cow";
  milkProduction: number;
}

function doSomethingWithAnimal(animal: IChicken | ICow): void {
  switch (animal.type) {
    case 'cow':
      alert(`the milk production is ${animal.milkProduction}`); // ok now
  }
}
Sign up to request clarification or add additional context in comments.

Comments

0

You can try below,

  doSomethingWithAnimal(animal: IChicken | ICow): void {
    if(animal.hasOwnProperty('eggsPerDay')) {
      console.log('eggsPerDay', animal.eggsPerDay)
    } else if (animal.hasOwnProperty('milkProduction')) {
      console.log('milkProduction', animal.milkProduction)
    }; // other conditions
  }

StackBlitz code for reference

4 Comments

I would recommend using an in instead, that also acts as a TS type guard:typescript-play.js.org/#code/…
I think that @TitianCernicova-Dragomir has an answer closer to what I was going for since animal.eggsPerDay would still cause en error.
@SethKillian, No problem. But what do you mean by animal.eggsPerDay would still cause en error.
A Typescript error. hasOwnProperty does not narrow the type of the parameter. That is why I recommend an in expression