Is it possible to narrow type output of a factory create method by literal types? I've managed to get some narrowing with if statements and discriminated unions, but this is creational method so I'm not sure whether it's possible.
class Radio {
type: "RADIO"; // literal type
title: string = "A value";
selected: boolean = false;
constructor(radio?: Radio) {
}
}
class OptionFactory {
static create({
type,
price = 1.0,
title = "Option",
selected = false,
}: {
price: number;
title: string;
selected: boolean;
}) {
switch (type) {
case "RADIO":
return new Radio({
title,
selected,
// price,
});
case "CHECKBOX":
return new Checkbox({
title,
selected,
// price,
});
case "PRICEOPTION":
return new PriceOption({
title,
selected,
price,
});
}
}
}
let radioButtons = new Array<Radio>();
tags.push(OptionFactory.create({ type: "RADIO" })); //error ts(2345)
console.log(tags);
CodePudding user response:
How about this...
function createFrom({ type, price = 1.0, title = "Option", selected = false }: CreationOptions): FactoryReturn {
return factoryMap[type]({ price, title, selected });
}
const factoryMap: FactoryMap = {
"RADIO": ({ title, selected }: Titleable & Selectable) => {
return new Radio({ title, selected });
},
"CHECKBOX": ({ title, selected }: Titleable & Selectable) => {
return new Checkbox({ title, selected });
},
"PRICEOPTION": ({ price, title, selected }: Titleable & Selectable & Priceable) => {
return new PriceOption({ price, title, selected });
}
}
type Typeable = { type: keyof FactoryMap };
type Priceable = { price?: number };
type Titleable = { title?: string };
type Selectable = { selected?: boolean };
type FactorySelector = Extract<CreationOptions, Typeable>;
type FactoryReturn = ReturnType<FactoryMap[FactorySelector["type"]]>;
type CreationOptions = Typeable & Priceable & Titleable & Selectable;
type RadioConstructor = (option: Titleable & Selectable) => Radio;
type CheckboxConstructor = (option: Titleable & Selectable) => Checkbox;
type PriceOptionConstructor = (option: Titleable & Selectable & Priceable) => PriceOption;
type FactoryMap = {
"RADIO": RadioConstructor,
"CHECKBOX": CheckboxConstructor,
"PRICEOPTION": PriceOptionConstructor
}
class Radio {
constructor(option: Titleable & Selectable) {
console.log('[Radio]', '[constructor]', option);
}
}
class Checkbox {
constructor(option: Titleable & Selectable) {
console.log('[Checkbox]', '[constructor]', option);
}
}
class PriceOption {
constructor(option: Titleable & Selectable & Priceable) {
console.log('[PriceOption]', '[constructor]', option);
}
}
console.log(createFrom({ type: "RADIO" }));
console.log(createFrom({ type: "CHECKBOX" }));
console.log(createFrom({ type: "PRICEOPTION" }));