How to deal with a `Variable 'xxx' is used before being assigned.`

Viewed 2137

I have a code block like below where I need to find something inside a loop, and also return a second variable. So I can't use a simple Array.find or Array.some good ole' for...of is my friend. map/filter don't allow a break and find can only return actual elements from the array, not a related calculation.

But the below within typescript is giving me an unavoidable error. I'm wondering if either there's a more idiomatic way to do this, or a better structure / place to declare the variable?

Variable 'found' is used before being assigned.

    let found: ParseResult
    // breaks when first item found but we capture a different value
    for (const rule of ParserRules) {
      // const rex = new RegExp(route.match)
      const parsed = rule.rex.exec(input)
      if (parsed) {
        found = { parsed, rule }
        break
      }
    }
    // @ts-ignore
    return found    // FIXME used before defined?

Here are the various JS iterator methods I tried...

const ar = [1, 2, 3, 4]

const log = console.log

const finder = (list) => {
  console.log('map', list.map(it => it === 3))
  console.log('find', list.find(it => it === 3))
  console.log('some', list.some(it => it === 3))
  console.log('filter', list.filter(it => it === 3))

  console.log('find', list.find(it => {
    if (it === 3) return it * 2 // value coerced to T|F
  }))

  console.log('filter', list.filter(it => {
    if (it === 3) return it * 2  // value coerced to T|F
  }))

  const arr = list.forEach((k) => {
    if (k === 3) return ('here')
  })
  log('arr', arr)

  let found
  for (const elem of list) {
    log('elem of', elem)
    if (elem === 2) {
      found = elem
      break
    }
  }
  log('found', found)

}

finder(ar)

2 Answers

The summary of your problem is that a function returning a value in a variable when, at times, the logic doesn't get a chance to assign any value to it.

You can either initialize the variable with a default value or, at the point of returning, check if it really has a value.

let found: ParseResult= {}

OR

return found || false    //Or an empty object etc

This can be done in many ways but what might suit your case would be

ar
  .map((rule) => {
    const parsed = rule.rex.exec(input)
    if (parsed) {
      return { parsed, rule }
    }
  })
  .find((x) => !!x)

Yes you are looping it once more but this is more readable. Also it would not be that costly.

If your processing is heavy, you can try this approach as well but this will be a custom implementation and will not come out of the box:

function getParsedValue(ar, input) {
  let parsed;
  const rule = ar
    .find((rule) => {
      parsed = rule.rex.exec(input);
      return !!parsed;
    })
  return !!rule ? { rule, parsed } : null
}
Related