I have a number of classes that I’ll need to create instances of based on a string. Each class has a model that needs to be passed in their constructor and they are all different (some similar properties, but we’ll keep it simple).
I am trying to create a method that allows 2 parameters; one to determine what class is to be instantiated, and the second parameter should be narrowed to the model needed for that class.
ie. if I pass “MenuElementEnum.Products” as the first param, you should only then be allowed to pass a ProductModel object as the second parameter.
const MenuElementEnum = {
Categories = 'Categories',
Products = 'Products',
}
type ProductModel = {
name:string;
age:number
id:string;
}
type CategoryModel = {
name:string;
favorite:boolean
id:string;
}
class Product implements ProductModel{
name =''
age= 0;
id = '';
constructor(model:ProductModel)
{
}
}
class Category implements CategoryModel{
name=''
favorite=true
id=''
constructor(model:CategoryModel)
{
}
}
const MenuElements = {
[MenuElementEnum.Products]:Product,
[MenuElementEnum.Categories]:Category
}
type KeysInEnum = keyof typeof MenuElementEnum
type ModelTypeForNewElement<Z extends keyof typeof MenuElements, Type extends typeof MenuElements[Z]> = {
[Prop in keyof Type]: Type[Prop];
}
type GEN = keyof typeof MenuElementEnum;
class DynamicMenuElement {
static instance: DynamicMenuElement;
public static get() {
if (!DynamicMenuElement.instance) {
DynamicMenuElement.instance = new DynamicMenuElement();
}
return DynamicMenuElement.instance;
}
public createMenuElement<T extends GEN>(type: T, model: ModelTypeForNewElement<T,typeof MenuElements[T]>) {
const ElementType = MenuElements[type]
if (!ElementType) {
throw new Error(`Cannot find the class type of ${type}`);
}
return new ElementType(model);
}
}
const creator = DynamicMenuElement.get();
creator.createMenuElement(MenuElementEnum.Categories,/*code hinting says a Category [model] is required here (expected) */)
creator.createMenuElement(MenuElementEnum.Product,/*code hinting says a Product [model] is required here (expected) */)
as you can see in this screenshot below, its merging “ProductModel” and “CategoryModel”…
What I expected was..
(model:ProductModel | CategoryModel) => Product | Category
I’ve tried all sorts of different approaches. This is the closest I’ve come. I don’t know why the argument “model” is being declared as all of my type aliases squished together.
(model:ProductModel | CategoryModel) => Product | Category
and not
(model:ProductModel & CategoryModel) => Product | Category