volna
volna

Reputation: 2610

How to deal with "deep" nested union types

In the logic below I am trying to type the data that is coming as balanceModel parameter to the function but straggle a bit with the deep nested model and will appreciate a lot if somebody can point out the flaw in the types below.

Here is a live example of the logic below in codesandbox: https://codesandbox.io/s/bold-meitner-vxto9

type BalanceModelAMDRType = {
  from: number;
  to: number;
  [index: string]: number;
};
type BalanceModelSectionType = {
  DRI: {
    AI: number;
    AMDR: BalanceModelAMDRType;
    EAR: number;
    RDA: number;
    UL: number;
    unit: string;
    [index: string]: string | number | BalanceModelAMDRType;
  };
};

type BalanceModelProgressSectionType = {
  DRI: {
    recommended: number;
    unit: string;
    [index: string]: string | number;
  };
};

type BalanceModelType = {
  energy: BalanceModelSectionType;
  [index: string]: BalanceModelSectionType | BalanceModelProgressSectionType;
};

function _updateEnergyDependentSections(
  balanceModel: BalanceModelType,
  energy: number
): void {
  const sections = [`mock`, `data`];

  sections.forEach(sectionName => {
    if (balanceModel[sectionName]) {
      const { DRI } = balanceModel[sectionName];

      Object.keys(DRI).forEach(DRIName => {
        switch (true) {
          case sectionName === `mock`:
            const AMDR = DRI[DRIName];

            Object.keys(AMDR).forEach(AMDRValueName => {
              const AMDRValue = AMDR[AMDRValueName];
              AMDR[AMDRValueName] = Math.round(
                AMDRValue * conversionMultiplier
              );
            });
            break;

          case sectionName === `data`:
            DRI[DRIName] = Math.round(DRI[DRIName] * conversionMultiplier);
        }
      });
    }
  });
}

Upvotes: 0

Views: 72

Answers (1)

mr.vea
mr.vea

Reputation: 454

If I understood the problem correctly.

const AMDR = DRI[DRIName]

could be string | number | BalanceModelAMDRType.

In mock section you want it be BalanceModelAMDRType and in data as number.

if you know for a fact that it will always be in those type in that moment, you add as type" to end, like

const AMDR = DRI[DRIName] as BalanceModelAMDRType; or
const AMDR = DRI[DRIName] as number;

if you do not know for a fact that in those instances the value will be in those types, you need to use a type guard(if statement).

if(typeof AMDR !== 'number' or typeof AMDR !== 'string'){ // AMDR is BalanceModelAMDRType }
if(typeof AMDR == 'number'){ //AMDR is number }

You can add a property to a type(interface), which will identify it as well, like:

type BalanceModelAMDRType = {
  type: "AMDR";
  from: number;
   to: number;
  } & { [index: string]: number };

and have a type guard function:

function isBalanceModelAMDRType(v: any): v is BalanceModelAMDRType {
  return "type" in v && v.type === "AMDR";
}

Upvotes: 1

Related Questions