Pete
Pete

Reputation: 668

Automatic detection of object property type based on another property value

I am trying to make a 'switcher' of an unknown type coming from a database. Basically, there are certain calculation types that have a different data structure and need to be processed differently depending on the type. My first way to solve the problem was using conditionals:

type CalculationTypes = "add" | "average"

export interface Calculation<T extends CalculationTypes> {
  calculationID: string // unique to all calculations
  type: T
  payload: T extends "add" ? CalculationAdd : CalculationAverage 
}

interface CalculationAdd {
  addlist: {referenceContainer: string, referenceID: string}[] 
}

interface CalculationAverage {
  averageList: number[]
}

This worked - kind of - because the type wasn't being detected later in the script. I had to infer it, which isn't so bad, but I'm sure there's a better way to do it where the transpiler can automatically deduce the type...

Then later in the script:

    for (const calculation of dbresult.calculations) {
      switch(calculation.type) {
        case "add":
          const payload = calculation.payload as CalculationAdd // <--- this is what I want it to do automatically.
          /** The transpiler does not detect it. It thinks that payload is of
              type CalculationAdd | CalculationAverage **/
          payload.addList // do stuff works now with type inference
          // do more stuff
          break;
        case "average":
          const payload = calculation.payload as CalculationAverage
          payload.averageList  // do stuff
          // do more stuff
          break;
      }
    }

Without type inference, typescript gives me the error:

Property 'addList' does not exist on type 'CalculationAdd | CalculationAverage'.
  Property 'addList' does not exist on type 'CalculationAverage'

So for some reason, typescript has not deduced that it has to be of type CalculationAdd without type inference, even though that's the only possibility given the conditional I set up in the Calculation interface.

How can I change the way I'm defining these types and interfaces so that typescript automatically deduces them?

Thanks!

Upvotes: 2

Views: 231

Answers (1)

Alexandru Somai
Alexandru Somai

Reputation: 1405

Typescript is actually capable of doing this. See this explanation, I think it's very similar with your use-case.

I think your problem comes from the fact that you've defined the same variable payload in both switch-case branches. Either try to rename the payload in the second case, or just wrap your switch-case branches in curly brackets, { ... }.

Here's the code that works for me:


for (const calculation of dbresult.calculations) {
    switch (calculation.type) {
        case "add": {
            const {payload} = calculation; // <--- automatically inferred by TS
            payload.addlist // do stuff works now with type inference
            console.log(payload);
            break;
        }
        case "average": {
            const {payload} = calculation;
            payload.averageList  // do stuff
            console.log(payload);
            break;
        }
    }
}

Upvotes: 1

Related Questions