```typescript
function validatePlan(
  tripDays: number[],
  passes: { span: number; cost: number }[],
): void {
  if (tripDays.length === 0) return;
  for (const day of tripDays) {
    if (!Number.isInteger(day) || day < 1) {
      throw new Error("trip days must be positive integers");
    }
  }
  for (let i = 1; i < tripDays.length; i += 1) {
    if (tripDays[i] <= tripDays[i - 1]) {
      throw new Error("trip days must be strictly increasing");
    }
  }
  if (passes.length === 0) {
    throw new Error("at least one pass kind is required");
  }
  for (const pass of passes) {
    if (!Number.isInteger(pass.span) || pass.span < 1) {
      throw new Error("pass span must be a positive integer");
    }
    if (!Number.isInteger(pass.cost) || pass.cost < 0) {
      throw new Error("pass cost must be a non-negative integer");
    }
  }
}

function cheapestPassPlan(
  tripDays: number[],
  passes: { span: number; cost: number }[],
): { total: number; purchases: number[][] } {
  validatePlan(tripDays, passes);
  if (tripDays.length === 0) return { total: 0, purchases: [] };

  // Sort passes by cost first, then by span in ascending order
  passes.sort((a, b) => a.cost - b.cost || a.span - b.span);

  let totalCost = 0;
  const purchases: number[][] = [];
  let currentPassStart = -1;

  for (const day of tripDays) {
    if (currentPassStart === -1) {
      // Find the earliest pass that covers the current day
      for (const { span, cost } of passes) {
        if (day >= currentPassStart + 1 && day <= currentPassStart + span) {
          totalCost += cost;
          purchases.push([currentPassStart + 1, span]);
          currentPassStart = currentPassStart + span - 1;
          break;
        }
      }
    } else {
      // Check if the next pass overlaps with the current one
      const { span, cost } = passes.find(pass => pass.span > 0);
      if (
        day >= currentPassStart + 2 &&
        day <= currentPassStart + span + 1 &&
        (currentPassStart === -1 || totalCost + cost < totalCost)
      ) {
        totalCost += cost;
        purchases.push([day, span]);
        currentPassStart = day + span - 1;
      }
    }
  }

  return { total: totalCost, purchases };
}
```