Utilizing pattern matching in switch statements

Imagine I have different APIs that provide data about various animals. Despite some shared properties, the JSON payloads for each animal type are quite unique and specific.

To avoid chaos in my code, I am looking to create strongly typed TypeScript classes for each distinct animal type. Each animal requires its own specialized handling!

What would be the most effective approach to achieve this? Ideally, I aim to implement something along these lines:

interface Animal {
    Name: string;
    Weight: number;
}

interface Insect extends Animal {
    AmountOfEyes: number;
}

interface Bird extends Animal {
    PlumageColor : string;
}

function OnlyForBirds(bird: Bird)
{
     // perform bird-related actions
}

function OnlyForInsects(insect: Insect)
{
     // do something insect-like
}


function GetAnimal(animalId: string) : Promise<Animal>
{
    const uri = `${baseURL}/${animalId}`;

    // fetches the json response body from http request
    const result = await get<any>(uri); 

    switch(animal.Name)
    {
        case  'Insect':
            return result as Insect;
        case ...
            ...
    }

    // throw unhandled error
}

function ProcessAnimal(animalId:string) : Promise
{
    let animal = await GetAnimal(animalId);
 
    // how can I handle this now? Can't I make use of a more convenient method over tye interfaces
    // rather than relying on .Name and re-casting?
    // Is there an industry standard I should follow?

    if(animal is a bird){  
        OnlyForBirds(bird)
    }

    else if(animal is an insect){
        OnlyForInsects(insect)
    }
}

I welcome any suggestions, even those that steer away from using interfaces in this manner.

Answer №1

In your specific scenario, the answer you provided seems to be the most suitable solution. I'd like to offer a different perspective on this issue. Your approach may encounter difficulties when dealing with multiple layers of inheritance, such as having Duck extend Bird. To determine if an Animal aligns with the base Bird interface, you can create a custom type guard function that inspects the object's properties, specifically looking for a PlumageColor. If present, TypeScript can confidently treat it as a Bird.

Here is a basic version of how this can be accomplished. By declaring the property PlumageColor as optional within the animal, we can safely access it even if it is undefined. The function checks if PlumageColor exists and is of type string.

const isBird = (animal: Animal & {PlumageColor?: any}): animal is Bird => {
  return typeof animal.PlumageColor === "string";
}

The following enhanced version using generics not only confirms that animal is a Bird but also retains any other previously known type information about the animal.

const isBird = <T extends Animal & {PlumageColor?: any}>(animal: T): animal is T & Bird => {
  return typeof animal.PlumageColor === "string";
}

Answer №2

I unraveled the mystery using some ancient sorcery. To harness the power, define an enum property within the core interface. Every distinct class, be it a creature or entity, will assign itself to one of these properties. Activate this property and unlock the potential of your specialized being.

enum BeingType {
    Entity = "Entity",
    Spirit = "Spirit"
}

interface Being {
    Type: BeingType;
    PowerLevel: number;
}

interface Entity extends Being {
    Type: BeingType.Entity; // UNLOCKING FORCES
    Strength: number;
}

interface Spirit extends Being {
    Type: BeingType.Spirit; // UNLOCKING FORCES
    EnergyColor : string;
}

function ExclusiveToSpirits(spirit: Spirit)
{
     // perform ethereal tasks
}

function ExclusiveToEntities(entity: Entity)
{
     // engage in tangible activities
}


function ObtainBeing(beingId: string) : Promise<Animal>
{
    const pathway = `${coreURL}/${beingId}`;

    // retrieves the data in JSON format from remote source
    const outcome = await get<any>(pathway); 

    switch(being.Type)
    {
        case  'Entity':
            return result as Entity;
        case ...
            ...
    }

    // catch errors
}

function AnalyzeBeing(beingId:string) : Promise
{
    let being = await ObtainBeing(beingId);

    switch(being.BeingType){
        case BeingType.Spirit:
            ExclusiveToSpirits(being); // within this scenario, the being is recognized as a Spirit! MAGIC!
            break;
        
        case BeingType.Entity:
            ExclusiveToEntities(being); // during this event, the being takes form as an Entity! Embark on your journey, little spirit!
            break;

        default:
            // anticipate unexpected occurrences
    }
}

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

When navigating using the next and back buttons, the active state in Angular is automatically removed

Looking for some assistance with my quiz app setup. Each question has True/False statements with corresponding buttons to select T or F. However, when I click the next/back button, the active class is not being removed from the previous selection. As a beg ...

Issue with calling a function to change the CSS color class of a button in Angular

Within my Angular code, I am attempting to set a CSS color for my button by calling a TypeScript function that will return the appropriate CSS class name. This is the code I have tried: <button style="height: 10%" class="getColor(days.date)">{{days ...

Tips for creating an input box that only accepts floating point numbers:

I have a custom component - a text box that I am using in two different places. In one location, it accepts integers and in another, floats. For the integer validation (where dataType=2), I have successfully implemented it. However, for the float validat ...

What is the best way to display just one record that has the lowest score out of all the records?

I need help with displaying only 1 record from the DL list that has the lowest score, instead of showing all records. In the example on stackblitz, you can see that for the first record, the DL scores are: 54, 20, and updated. Instead of displaying all 3 ...

Utilize the class type of a method parameter as the method type for another parameter

Here's a quick example illustrating my desired functionality. // Every time, the ACL class will have a different name such as "UsersACL", etc. export class EventsACL { test(): { read: true, write: true } { } } // This function acts ...

What is the best way to filter specific data types when using ngFor in Angular?

As I loop through the array named "fruits," which contains objects of type "FruitService" that I created, I want to display each element. However, when I delete them (I know it's strange, no database involved), they turn into type "undefined" and star ...

Inter-component communication in Angular

I am working with two components: CategoryComponent and CategoryProductComponent, as well as a service called CartegoryService. The CategoryComponent displays a table of categories fetched from the CategoryService. Each row in the table has a button that r ...

Most effective methods for validating API data

Currently, I am working on developing an api using nestjs. However, I am facing some confusion when it comes to data validation due to the plethora of options available. For instance, should I validate data at the route level using schema validation (like ...

Tips for successfully mocking axios.get in Jest and passing AxiosPromise type value

I attempted to simulate the axios.get() function using the code below, however TypeScript is returning an error stating "argument of type '{ data: expectedResult }' is not assignable to parameter of type 'AxiosPromise<{}>'". Can ...

The best practices for utilizing ES6 Modules syntax in TypeScript files within a NextJS environment

The issue appears to be trapped in a loop: package.json is missing type: "module". This results in an error when trying to use modules in TypeScript files: An error occurred while running the seed command: /Users/me/code/me/prisma-learning/grap ...

Is there a shortcut for creating interfaces that have identical sub properties?

We are seeking to streamline the interface creation process by utilizing shorthand for properties labeled from Monday through Sunday, each with identical sub-properties. interface Day { start: number end: number } interface Schedule { Monday: Day ...

Obtain access to the child canvas element within the parent component

I'm currently working on an app that allows users to sign with a digital pointer. As part of the project, I have integrated react-canvas-signature. My next task is to capture the signature from the canvas and display it in a popup. According to thei ...

Total the values of several items within the array

Here is the data I currently have: const arrayA = [{name:'a', amount: 10, serviceId: '23a', test:'SUCCESS'}, {name:'a', amount: 9, test:'FAIL'}, {name:'b', amount: ...

Setting up a React state with an Object using Typescript: A step-by-step guide

As someone who is new to TypeScript, I have a solid understanding of how to set up an interface for certain types. However, I am struggling to comprehend how the same concept would apply to an Object type. interface MyProps { defaultgreeting: 'He ...

What causes the select dropdown to display an empty default in Angular 8 following an HTTP request?

I have created a simple HTML code to populate array elements in a dropdown list, with the default value being fetched from an HTTP service during the ngOnInit lifecycle hook. However, I am encountering an issue where the default value is displayed as empty ...

What would be the optimal type for the second argument of the `simulate` method?

When using the simulate function, I am familiar with code like this: simulate("change", { target: { value: '7' } }); However, if my onChange function requires an object as a parameter, what should I pass in the second argument? interface myObj ...

What are the best methods for protecting a soda?

My code is in strict mode, and I am encountering an issue with the following snippet: const a: string[] = []; // logic to populate `a` while (a.length > 0) { const i: string = a.pop(); // This line is causing an error console.log(i); // additio ...

Should ts-node be avoided in a production environment due to potential risks?

My current setup involves using ts-node with express in production and so far, it's been functioning smoothly. Am I missing out on any benefits by not compiling and running .js files instead? ...

What could be causing the lack of updates in my SolidJS component when the data changes?

One of my components in SolidJS is an Artist page component. Here is a snippet of the code: export function Artist() { const params = useParams<{ id: string }>(); const [data, setData] = createSignal(null); createEffect(() => { fetchArti ...

Encountering an issue with MUI Props: "Must provide 4 to 5 type arguments."

I'm attempting to use a custom component and pass in AutocompleteProps as a prop, all while utilizing typescript. Here's my current setup: type Props = { autoCompleteProps?: AutocompleteProps<T> label: string loading?: boolean } cons ...