Home > Enterprise >  Branch off based on the type of an object
Branch off based on the type of an object

Time:05-21

In TypeScript, how can I check the type of an object, and branch off based on that type?

const createAnimalForId(animalId: AnimalIdsEnum): CatAnimal | BirdAnimal {

    // animalData has type: CatAnimalData | BirdAnimalData | undefined
    const animalData = animalsMap.get(animalId);
  
    // Q: How can I check the type here?
    if(animalData "is of type" CatAnimalData) {
         // NB: I want it to be aware that animalData here is of type CatAnimalData
        return new CatAnimal(animalData);
    }

    // Q: How can I check the type here?
    else if(animalData "is of type" BirdAnimalData) {
        // NB: I want it to be aware that animalData here is of type BirdAnimalData
        return new BirdAnimal(animalData);
    }

    else {
        throw new RangeError("Animal type not recognised");
    }
}

NB: CatAnimal and BirdAnimal both inherit from AbstractAnimal.

CodePudding user response:

how can I check the type of an object?

Since these types are implemented as classes, you can just use instanceof:

if (animalData instanceof CatAnimalData) {
     …
}
if (animalData instanceof BirdAnimalData) {
    …
}

… and branch off based on that type?

Don't. This is an ugly, non-extensible design. Instead, make all your inputs implement a toAnimal or getAnimal or createAnimal method, and then just call that:

export class AbstractAnimalData {
    name: string;
    abstract createAnimal(): Animal {
        throw new RangeError("Animal type not recognised");
    }
}

export class CatAnimalData extends AbstractAnimalData {
    favouriteFood: string;
    // NB: Leaving out the constructor for brevity
    createAnimal(): CatAnimal {
        return new CatAnimal(this);
    }
}

export class BirdAnimalData extends AbstractAnimalData {
    archEnemy: string;
    // NB: Leaving out the constructor for brevity
    createAnimal: BirdAnimal {
        return new BirdAnimal(animalData);
    }
}

then

function createAnimalForId(animalId: AnimalIdsEnum): Animal {
    const animalData = animalsMap.get(animalId);
    if (!animalData) {
        throw new RangeError(`AnimalId '${animalId}' not supported`);
    }
    return animalData.createAnimal();
    //               ^^^^^^^^^^^^^^^
}

CodePudding user response:

You can check if property in exists in animalType

const createAnimalForId(animalId: AnimalIdsEnum): CatAnimal | BirdAnimal {

    // animalData has type: CatAnimalData | BirdAnimalData | undefined
    const animalData = animalsMap.get(animalId);
  
    
    if('favouriteFood' in animalData) {
         // CatAnimalData
        return new CatAnimal(animalData);
    }

   
    else if("archEnemy" in animalData) {
        //BirdAnimalData
        return new BirdAnimal(animalData);
    }

    else {
        throw new RangeError("Animal type not recognised");
    }
}

CodePudding user response:

One solution I can think of would be the following. However, this has the problem that, within the conditional cases, TypeScript is not aware of the specific type of animalData.

As far as I understand, this means I would have to manually cast it, which I don't think is ideal.

const createAnimalForId(animalId: AnimalIdsEnum): CatAnimal | BirdAnimal {

    // animalData has type: CatAnimalData | BirdAnimalData | undefined
    const animalData = animalsMap.get(animalId);
  
    if(animalData.type === animalType.catAnimal) {
         // Problem: TypeScript is not aware of the specific type of animalData here
        return new CatAnimal(
            animalData // as CatAnimalData
        ); 
    }

    else if(animalData.type === animalType.birdAnimal) {
        // NB: I want it to be aware that animalData here is of type BirdAnimalData
        return new BirdAnimal(
            animalData // as BirdAnimalData
        );
    }

    else {
        throw new RangeError("Animal type not recognised");
    }
}

CodePudding user response:

Use an instanceof type guard:

const createAnimalForId(animalId: AnimalIdsEnum): CatAnimal | BirdAnimal {

    // animalData has type: CatAnimalData | BirdAnimalData | undefined
    const animalData = animalsMap.get(animalId);
  
    if(animalData instanceof CatAnimalData) {
        // animalData here is of type CatAnimalData
        return new CatAnimal(animalData);
    }

    else if(animalData instanceof BirdAnimalData) {
        // animalData here is of type BirdAnimalData
        return new BirdAnimal(animalData);
    }

    else {
        throw new RangeError("Animal type not recognised");
    }
}
  • Related