```ts
export function regionTotals(grid: number[][], queries: number[][]): number[] {
  if (!Array.isArray(grid) || grid.length === 0) {
    throw new Error("grid must be a non-empty list of rows");
  }
  const width = Array.isArray(grid[0]) ? grid[0].length : 0;
  if (width === 0) {
    throw new Error("rows must be non-empty lists");
  }
  for (const row of grid) {
    if (!Array.isArray(row) || row.length !== width) {
      throw new Error("rows must all share one length");
    }
    for (const cell of row) {
      if (!Number.isInteger(cell)) {
        throw new Error("cells must be integers");
      }
    }
  }

  const prefixSumTable = buildPrefixSumTable(grid);

  return queries.map(query => {
    const [top, left, bottom, right] = query;
    if (top >= bottom || left >= right) {
      throw new Error("query bounds are invalid");
    }
    if (top < 0 || left < 0 || bottom >= grid.length || right >= width) {
      throw new Error("query reaches outside the grid");
    }

    return prefixSumTable[bottom][right] - (prefixSumTable[top - 1]?.[right] ?? 0) - (prefixSumTable[bottom]?.[left - 1] ?? 0) + (prefixSumTable[top - 1]?.[left - 1] ?? 0);
  });
}

function buildPrefixSumTable(grid: number[][]): number[][] {
  const height = grid.length;
  const width = grid[0].length;
  const prefixSumTable = Array.from({ length: height + 1 }, () => Array(width + 1).fill(0));

  for (let i = 1; i <= height; i++) {
    for (let j = 1; j <= width; j++) {
      prefixSumTable[i][j] = grid[i - 1][j - 1] + prefixSumTable[i - 1][j] + prefixSumTable[i][j - 1] - prefixSumTable[i - 1][i - 1];
    }
  }

  return prefixSumTable;
}
```