How to effectively handle multiple conditional statements in TypeScript?

I attempted to implement a "multiple filter" feature in TS, so...

If I don't provide any input -> all products are returned as usual;

However, if I specify some parameters -> only products matching the parameters are returned.

(I used colors for demonstration purposes)

async load(params: params): LoadProducts.Result => {
  const products = [];

  if (params) {
    if (params.red != undefined) // products.push( ...load.redProducts() );
    if (params.blue != undefined) // products.push( ...load.blueProducts() );
    if (params.green != undefined) // products.push( ...load.greenProducts() );
    if (params.yellow != undefined) // products.push( ...load.yellowProducts() );
  } else {
    // products.push( ...load.products() );
  }

  return products;
}

Type of request:

type params = {
  filter?: {
    red?:  boolean;
    blue?: boolean;
    green?: boolean;
    yellow?: boolean;
  };
};

Example of request:

{
  "filter": {
    "red": true,
    "blue": true
  }
}

How can I handle multiple IF statements to check the parameters, as each one corresponds to a different function for loading, and I intend to include more filters in the future.

Answer №1

When dealing with an abundance of filters, the key is to avoid redundancy at all costs. The best approach would be to have the loading functions drive the entire process: You can establish an object containing these functions as const, and then derive your various filters from that central source. Below is a practical example with explanatory comments included:

// Placeholder for defining product type
type Product = {
    name: string;
    type: string;
};

// Individual loading functions for different types
const loaders = {
    async red() {
        return [{ type: "red", name: "Red 1" }];
    },
    async blue() {
        return [{ type: "blue", name: "Blue 1" }];
    },
    async green() {
        return [{ type: "green", name: "Green 1" }];
    },
    async yellow() {
        return [
            { type: "yellow", name: "Yellow 1" },
            { type: "yellow", name: "Yellow 2" },
        ];
    },
} as const;

// Function to load all products
const loadAll = async () => {
    const allLoaders = Object.values(loaders);
    return (
        await Promise.all(allLoaders.map(loader => loader()))
    ).flat();
};

// Types of filters available
type FilterType = keyof typeof loaders;

// Parameter types for filters
type ParamsFilters = {
    [Key in FilterType]?: boolean;
};
type Params = {
    filter?: ParamsFilters;
};

// Function to validate if provided key is a valid loader key
function assertIsLoaderKey(key: string): asserts key is keyof typeof loaders {
    if (!(key in loaders)) {
        throw new Error(`Expected key for 'loaders', got "${key}"`);
    }
}

// Class where the functionality resides
class Example {
    async load(params: Params): Promise<Product[]> {
        let products: Product[];

        // Processing filters if present
        if (params.filter) {
            // Executing selected filters
            products = (
                await Promise.all(
                    Object.entries(params.filter)
                        .filter(([key, flag]) => flag)
                        .map(([key]) => {
                            assertIsLoaderKey(key);
                            return loaders[key]();
                        })
                )
            ).flat();
        } else {
            // No filters, load everything
            products = await loadAll();
        }

        return products;
    }
}

// Sample usage scenario
const e = new Example();
e.load({
    filter: {
        red: true,
        blue: true,
    },
});

Playground link

(This demonstration incorporates the use of the relatively-new flat method. While widely supported by modern browser versions, older browsers may require polyfilling.)

Answer №2

Although a minimal, reproducible example is missing, you can consider utilizing an array of tuples and iterating through it based on the information provided:

const fruitFns = [
  ['apple', load.appleProducts.bind(load)],
  ['orange', load.orangeProducts.bind(load)],
  // etc...
] as const;

async load(params: params): LoadProducts.Result => {
  const products = [];

  if (params) {
    for (const [fruit, fn] of fruitFns) {
      if (params[fruit] != undefined) products.push(...fn());
    }
  } else {
    // products.push( ...load.products() );
  }

  return products;
}

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

Utilizing NGRX reducers with a common state object

Looking for a solution with two reducers: export function reducer1(state: State = initialState,: Actions1.Actions1); export function reducer2(state: State = initialState,: Actions2.Actions1); What I want is for both reducers to affect the same state objec ...

Issues with incorrect source path in Typescript, Gulp, and Sourcemaps configuration

In my nodejs app, the folder structure is as follows: project |-- src/ | |-- controllers/ | | |`-- authorize-controller.ts | |`-- index.ts |--dist/ | |--controllers/ | | |`-- authorize-controller.js | | |`-- authorize-controller.js.map | ...

The module "angular2-multiselect-dropdown" is experiencing a metadata version mismatch error

Recently, I updated the node module angular2-multiselect-dropdown from version v3.2.1 to v4.0.0. However, when running the angular build command, I encountered an "ERROR in Metadata version mismatch for module". Just to provide some context, I am using yar ...

Needing to utilize the provide() function individually for every service in RC4

In Beta, my bootstrapping code was running smoothly as shown below: bootstrap(App, [ provide(Http, { useFactory: (backend: XHRBackend, defaultOptions: RequestOptions, helperService: HelperService, authProvider: AuthProvider) => new CustomHt ...

There are no HTTP methods available in the specified file path. Make sure to export a distinct named export for each HTTP method

Every time I attempt to run any code, I encounter the following error message: No HTTP methods exported in 'file path'. Export a named export for each HTTP method. Below is the content of my route.ts file: import type { NextApiRequest, NextApi ...

Could JOI be used to validate unidentified keys within nested data structures?

I've developed a middleware that uses Joi to validate incoming requests. export default (schema: any) => async (req: Request, res: Response, next: NextFunction) => { try { const validation = schema.validate(req, { abortEarly: false }) ...

Update to using res.get() instead of res.getHeader()

Looking for assistance with the code snippet below: const compress = require('compression'); export const handleCompression = compress({ filter: function (req: express.Request, res: express.Response) { return (/json|text|javascript|css|fo ...

Displaying a child component as a standalone page rather than integrating it within the parent component's body

I'm currently working on implementing nested navigation in my website, but I am facing challenges with loading the child component without the need to include a router-outlet in the parent component. This setup is causing the child component's co ...

Is it possible to implement a redirect in Angular's Resolve Navigation Guard when an error is encountered from a resolved promise?

I have integrated Angularfire into my Angular project and am utilizing the authentication feature. Everything is functioning properly, however, my Resolve Navigation Guard is preventing the activation of the component in case of an error during the resolve ...

Is there a distinction in Typescript between the return types of Object.seal and .freeze?

I am looking to safeguard the constant object content from any changes during runtime. This includes alterations to both the object structure and its content. The preferred method for achieving this is by using Object.freeze. interface iRO<T> { r ...

Passing an observable from parameters to a pipe in RxJS: A guide

Can someone help me with writing a TypeScript function like the one below: function abc(arg1, arg2, arg3) { pipe(arg1, arg2, arg3...); // or someSubject.pipe(arg1, arg2, arg3..) } I keep getting errors when trying to build the code. How can I success ...

What are the steps to integrate TypeScript into JavaScript code?

How can I import a TypeScript class in a Node CommonJS JavaScript file? When using mongoose in my TypeScript code, I typically do the following: // user.model.ts export const UserModel = model<User>('User', schema); In my JavaScript code: ...

Conditional Rendering with Next.js for Smaller Displays

I've implemented a custom hook to dynamically render different elements on the webpage depending on the screen size. However, I've noticed that there is a slight delay during rendering due to the useEffect hook. The conditionally rendered element ...

Interactive Typescript slider with drag functionality

Seeking assistance in developing a draggable slider using TypeScript. I have managed to retrieve the client and scroll positions, but I am struggling to change the ref scrollLeft position. There are no errors thrown; however, the ref's scrollLeft prop ...

Is it feasible to alter the file name while utilizing express-fileUpload library?

Is there a way to modify the file name of an uploaded file on the server side? app.post(URL, (req, res) => { let fileName = req.files.file.name; req.fileUpload; res.statusCode = HTTP_OK; res.send("Good Job") }) The settings I have in uploadF ...

What is the best way to change JSON into a key-value dictionary with TypeScript?

Here is a JSON example that I am working with: { "MyTest:": [{ "main": { "name": "Hello" }, "test2": { "test3": { "test4": "World" }, ...

I am encountering issues with the TypeScript repository build on my local machine, but it successfully passes when

I am encountering an issue with a TypeScript repository failing to build on my local machine. The error message I receive is as follows: $ tsc --pretty -p tsconfig.json ../../../../../../node_modules/@types/graphql/subscription/subscribe.d.ts:17:12 - erro ...

The Typescript error message states that the type '{ onClick: () => void; }' cannot be assigned to the type 'IntrinsicAttributes'

I'm a beginner in Typescript and I'm encountering difficulties comprehending why my code isn't functioning properly. My goal is to create a carousel image gallery using React and Typescript. However, I'm facing issues when attempting t ...

PrimeNG's Angular component pTree TreeNode

Is there a way in Angular to retrieve the positions of nodes within a TreeNode hierarchy based on their display order? I have data structured as TreeNode objects, which include children that can branch off further. How can I access these nodes according t ...

Learn how to manipulate Lit-Element TypeScript property decorators by extracting values from index.html custom elements

I've been having some trouble trying to override a predefined property in lit-element. Using Typescript, I set the value of the property using a decorator in the custom element, but when I attempt to override it by setting a different attribute in the ...