In my database I have a product_type ('prod1' | 'prod2' | 'prod3').
I would like to generate a class object based on its type.
Here's my TypeScript code:
interface Product {
readonly type: string;
name(): string;
}
class Product1 implements Product {
readonly type: string = 'prod1';
name: () => 'Product 1';
}
class Product2 implements Product {
readonly type: string = 'prod2';
name: () => 'Product 2';
}
class Product3 implements Product {
readonly type: string = 'prod3';
name: () => 'Product 3';
}
function getProductByType(type: string) {
// TODO: return Product1, Product2 or Product3
if (type == 'prod1') return new Product1();
else if (type == 'prod2') return new Product2();
else return new Product3();
}
The problem is in the getProductByType
function.
Is there an approach to return a concrete Product class based on the passed type without having multiple if-else statements?
This sounds like a good case for a factory strategy pattern but I can't figure out how to correctly implement it here...
There are several possible solutions, it depends how much "automated" the solution should be.
Using mapping, similar to AdityaParab's answer.
class Product1 {}
class Product2 {}
class Product3 {}
// Mapping from type name to class object.
const ProductMap = {
prod1: Product1,
prod2: Product2,
prod3: Product3
};
function getProductByType(type) {
const Product = ProductMap[type];
if (!Product) throw new Error(`Unknown ProductType '${type}'.`);
return new Product(type);
}
console.log(getProductByType("prod1"));
StackBlitz demo here.
Check out tst-reflection GitHub repo.
Decorator used to mark Product classes.
/**
* Decorator used to mark Product classes.
* @reflect - required JSDoc property. Means all decorated types can be used in reflection.
* @param productType
*/
export function ForProductType(productType: string) {
return (ctor: Function) => {};
}
Decorated Product class.
@ForProductType('prod1')
export class Product1 implements Product {
readonly type: string = 'prod1';
get name() {
return 'Product 1';
}
}
getProductByType function with a little bit of reflection.
// Some reflection job.. Find all types decorated by the ForProductType decorator and create map of those types.
const entries = Type.getTypes()
.map<[string, Type]>((type) => [
type
.getDecorators()
.find((decorator) => decorator.name == 'ForProductType')
?.getArguments()[0],
type,
])
.filter(([typeName, type]) => !!typeName);
const ProductTypeMap = new Map<string, Type>(entries);
function getProductByType(type: string): Promise<Product> {
const ProductType: Type = ProductTypeMap.get(type);
if (!ProductType) {
throw new Error(`Unknown ProductType '${type}'.`);
}
return ProductType.getCtor().then((Product) => new Product());
}
Usage
getProductByType('prod1').then((product) => console.log(product, product.name));
It returns Promise cuz it does dynamic imports of the Product classes.