```ts
export function layoutColumns(rows: string[][], aligns: string): string[] {
  if (rows.length === 0) throw new Error("Empty table");

  const numCols = rows[0].length;
  if (aligns.length !== numCols) throw new Error("Alignment spec length mismatch");

  for (const row of rows) {
    if (row.length !== numCols) throw new Error("Row cell count mismatch");
    for (const cell of row) {
      if (typeof cell !== "string") throw new Error("Non-string cell found");
    }
  }

  const colWidths = Array(numCols).fill(0);
  for (const row of rows) {
    for (let i = 0; i < numCols; i++) {
      colWidths[i] = Math.max(colWidths[i], row[i].length);
    }
  }

  return rows.map(row => {
    let line = "";
    for (let i = 0; i < numCols; i++) {
      const cell = row[i];
      const width = colWidths[i];
      switch (aligns[i]) {
        case "l":
          line += cell.padEnd(width);
          break;
        case "r":
          line += cell.padStart(width);
          break;
        case "c":
          const leftPad = Math.floor((width - cell.length) / 2);
          const rightPad = width - cell.length - leftPad;
          line += " ".repeat(leftPad) + cell + " ".repeat(rightPad);
          break;
      }
      if (i < numCols - 1) line += "  ";
    }
    return line.trimEnd();
  });
}
```