Working with loops

Author: FunctionDJCreated May 18, 2026Updated May 18, 2026

I struggly a lot converting a code base to using neverthrow in places where functions are called inside of loops.

Example:

javascript
function damageUnitsOnCityTiles(player) {
  for (const unit of player.team.getEnemyUnits()) {
    if (unit.getTile().type === "city") { // <-- previously, getTile() used throw, but now it returns Result<Tile, E>, so this code expectedly errors
      unit.damageUnit(3)
    }
  }
}

How do I rewrite such code to hand over any error upwards? In this example I'd prefer aborting early if an error is encountered and returning it (no need to try all calls and collect all errors).


One possible solution (I think) using safeTry():

javascript
function damageUnitsOnCityTiles(player) {
  return safeTry(function* () {
    for (const unit of player.team.getEnemyUnits()) {
      const tile = yield* unit.getTile() // eslint-plugin-neverthrow error: Result must be handled with either of match, unwrapOr or _unsafeUnwrap.
      if (tile.type === "city") {
        unit.damageUnit(3)
      }
    }

    return ok()
  })
}

Cons: Lint error + i don't really like generators


Another solution using Result.combine():

javascript
function damageUnitsOnCityTiles(player) {
  const unitsAndTilesResult = Result.combine(
    player.team.getEnemyUnits().map((unit) => unit.getTile().map((tile) => ({ unit, tile }))),
  );

  return unitsAndTilesResult.map((unitsAndTiles) => {
    for (const { unit, tile } of unitsAndTiles) {
      if (tile.type === "city") {
        unit.damageUnit(3)
      }
    }
  });
}

Pros: No lint error Cons: No abort-early + very boilerplate-y with the unitsAndTilesResult bag variable