I was trying to do a "multiple filter" in TS, So...
If i send nothing -> it returns all products normally;
But if i send some params -> it returns products filtered by the params.
(I used colors just for the example)
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 deal with the multiple IFs to verify the params, because each has its own function to load and I want to add more filters.
CodePudding user response:
You have not included a minimal, reproducible example, but based on what you have shown, you can use a (map-like) array of tuples and iterate over it:
const colorFns = [
['red', load.redProducts.bind(load)],
['blue', load.blueProducts.bind(load)],
// etc...
] as const;
async load(params: params): LoadProducts.Result => {
const products = [];
if (params) {
for (const [color, fn] of colorFns) {
if (params[color] != undefined) products.push(...fn());
}
} else {
// products.push( ...load.products() );
}
return products;
}
CodePudding user response:
If there are lots of filters, the primary thing is probably to avoid repeating yourself. So I would make the functions that do the loading drive the process: You define an object with those functions as const
, then derive your possible filters from that. Here's an example (see inline comments):
// Placeholder for your product type
type Product = {
name: string;
type: string;
};
// The functions that do the loading of individual 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;
// A function that loads everything
const loadAll = async () => {
const allLoaders = Object.values(loaders);
return (
await Promise.all(allLoaders.map(loader => loader()))
).flat();
};
// The types of filters we might have
type FilterType = keyof typeof loaders;
// The params type
type ParamsFilters = {
[Key in FilterType]?: boolean;
};
type Params = {
filter?: ParamsFilters;
};
// A function that asserts a string key is a valid key for `loaders`
function assertIsLoaderKey(key: string): asserts key is keyof typeof loaders {
if (!(key in loaders)) {
throw new Error(`Expected key for 'loaders', got "${key}"`);
}
}
// The class this is apparently in
class Example {
async load(params: Params): Promise<Product[]> {
let products: Product[];
// If we have filters...
if (params.filter) {
// ...run them
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;
}
}
// Example usage
const e = new Example();
e.load({
filter: {
red: true,
blue: true,
},
});
(That uses the relatively-new flat
method. It's supported by even vaguely recent versions of modern browsers, but may need polyfilling for old ones.)