```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 prefixSum = grid.map((row, rowIndex) => {
    return row.map((cell, colIndex) => {
      let sum = cell;
      if (rowIndex > 0) {
        sum += prefixSum[rowIndex - 1][colIndex];
      }
      if (colIndex > 0) {
        sum += prefixSum[rowIndex][colIndex - 1];
      }
      if (rowIndex > 0 && colIndex > 0) {
        sum -= prefixSum[rowIndex - 1][colIndex - 1];
      }
      return sum;
    });
  });

  const result = queries.map(([top, left, bottom, right]) => {
    if (!Array.isArray([top, left, bottom, right]) || [top, left, bottom, right].length !== 4) {
      throw new Error("queries must be lists of exactly four integers");
    }
    const [t, l, b, r] = [top, left, bottom, right];
    if (t >= b || l >= r) {
      throw new Error("query top must be strictly less than query bottom and query left must be strictly less than query right");
    }

    // Handle boundary cases where queries might go out of bounds
    const adjustedTop = Math.max(t, 0);
    const adjustedLeft = Math.max(l, 0);
    const adjustedBottom = Math.min(b, prefixSum.length);
    const adjustedRight = Math.min(r, width);

    let blockTotal = 0;

    for (let i = adjustedTop; i < adjustedBottom; i++) {
      for (let j = adjustedLeft; j < adjustedRight; j++) {
        blockTotal += prefixSum[i][j];
      }
    }

    return blockTotal;
  });

  return result;
}
```