Search code examples
javascriptvue.jsvuejs3conditional-operator

Any way to use 'defined' operator on object keys?


I have a v-if condition that fails if a state is undefined. The state is nested and pretty large (10k+ entries) and might take time to be constructed. A state's subitem [0].expand of boolean type has to be checked in order to pass the v-if condition:

v-if="
  // condition required to prevent index [0] from failing
  (state.priceClassData[type]
    ? state.priceClassData[type][0].expand
    : false)
  "

I've been trying to refactor this into something more human-readable. In production there are multiple such condition checks in one ternary operator and I am trying to find a way to replace it.

The easiest thing I thought would be to use the ? defined operator on state.priceClassData.type? but as you can see, type in my case is a key and has to be called like so:

state.priceClassData[type][0]

I've not found a way to apply ? here. Other attempts include computed:

const checkCondition = computed(() => {
  if (state.priceClassData[type] && state.priceClassData[type][0]) {
    return state.priceClassData[type][0].expand
  }
  return false
})

but I can't find a way to pass the type parameter into it, making it always return true. I've also tried ordinary functions:

function checkCondition(type) {
  return (state.priceClassData[type] && state.priceClassData[type][0])
    ? state.priceClassData[type][0].expand
    : false
}

but it only seems to be executed initially, returning false and never run again.

I've also tried combining functions and computed:

const checkCondition = (type) => computed(() => {
  return (state.priceClassData[type] && state.priceClassData[type][0])
    ? state.priceClassData[type][0].expand
    : false;
}

which always returns true or:

const checkCondition = computed({
  get() {
    return state.priceClassData
  },
  set() {
    return (state.priceClassData[type] && state.priceClassData[type][0])
    ? state.priceClassData[type][0].expand
    : false;
  }
})

which doesn't seem to accept the syntax.


Solution

  • You can use optional chaining and nullish coalescing:

    state.priceClassData[type]?.[0].expand ?? false
    

    You can easily add more ? along the chain of property accesses to guard against intermediate values being null or undefined.

    Note however, that you don't actually need to have the whole expression be strictly equal to false for v-if. undefined is also falsy, so v-if will not render the element if it's passed an expression evaluating to undefined. Thus v-if="state.priceClassData[type]?.[0].expand" should suffice.