Home > Software engineering >  Factory narrowing inside switch statement
Factory narrowing inside switch statement

Time:03-05

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);

Typescript Playground

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" }));

WYSIWYG => WHAT YOU SHOW IS WHAT YOU GET

  • Related