```ts
export function turnCostRoute(grid: number[][], stepCost: number, turnCost: number): number {
  if (grid.length === 0 || grid.some(row => row.length !== grid[0].length)) {
    throw new Error("Invalid grid: empty or ragged");
  }
  if (stepCost <= 0 || !Number.isInteger(stepCost)) {
    throw new Error("Invalid stepCost: must be a positive integer");
  }
  if (turnCost < 0 || !Number.isInteger(turnCost)) {
    throw new Error("Invalid turnCost: must be a non-negative integer");
  }

  const numRows = grid.length;
  const numCols = grid[0].length;

  if (grid[0][0] === 1 || grid[numRows - 1][numCols - 1] === 1) {
    return -1;
  }
  if (numRows === 1 && numCols === 1) {
    return 0;
  }

  const directions = [
    [0, 1], // right
    [1, 0], // down
    [0, -1], // left
    [-1, 0] // up
  ];

  interface CellState {
    cost: number;
    direction: number | null; // Index of the direction array or null for the start
  }

  const dp: CellState[][] = Array.from({ length: numRows }, () =>
    Array.from({ length: numCols }, () => ({ cost: Infinity, direction: null }))
  );

  dp[0][0] = { cost: 0, direction: null };

  for (let row = 0; row < numRows; row++) {
    for (let col = 0; col < numCols; col++) {
      if (grid[row][col] === 1) continue;

      for (const [dr, dc] of directions) {
        const newRow = row + dr;
        const newCol = col + dc;

        if (newRow >= 0 && newRow < numRows && newCol >= 0 && newCol < numCols && grid[newRow][newCol] === 0) {
          const nextCost = dp[row][col].cost + stepCost;
          let totalCost = nextCost;

          if (dp[row][col].direction !== null && dp[row][col].direction !== directions.indexOf([dr, dc])) {
            totalCost += turnCost;
          }

          if (totalCost < dp[newRow][newCol].cost) {
            dp[newRow][newCol] = { cost: totalCost, direction: directions.indexOf([dr, dc]) };
          }
        }
      }
    }
  }

  return dp[numRows - 1][numCols - 1].cost === Infinity ? -1 : dp[numRows - 1][numCols - 1].cost;
}
```