```ts
export function spreadNightDuty(crew: string[], weights: number[], away: string[][]): string[] {
  if (crew.length === 0) {
    throw new Error("Crew cannot be empty");
  }
  if (new Set(crew).size !== crew.length) {
    throw new Error("Crew contains duplicate names");
  }
  if (!crew.every(name => typeof name === "string" && name !== "")) {
    throw new Error("Crew must contain non-empty string names only");
  }
  if (weights.some(weight => weight !== 1 && weight !== 2)) {
    throw new Error("Weights must be either 1 or 2");
  }
  if (away.length !== weights.length) {
    throw new Error("Away list must be the same length as weights");
  }
  away.forEach(day => {
    if (!Array.isArray(day)) {
      throw new Error("Each entry in away must be an array");
    }
    day.forEach(name => {
      if (!crew.includes(name)) {
        throw new Error(`Name ${name} in away list is not in the crew`);
      }
    });
  });

  const stood = new Map<string, number>();
  for (const name of crew) {
    stood.set(name, 0);
  }

  const nights: string[] = [];
  let previousResting = new Set<string>();

  for (let night = 0; night < weights.length; night++) {
    let chosen = "";
    let minLoad = Infinity;

    for (const name of crew) {
      if (away[night].includes(name) || previousResting.has(name)) {
        continue;
      }
      const currentLoad = stood.get(name) as number;
      if (currentLoad < minLoad || (currentLoad === minLoad && name < chosen)) {
        minLoad = currentLoad;
        chosen = name;
      }
    }

    if (chosen === "") {
      nights.push("?");
    } else {
      nights.push(chosen);
      stood.set(chosen, (stood.get(chosen) as number) + weights[night]);
      previousResting = new Set([chosen, ...(previousResting.has(chosen) ? [] : [night])].map(n => n + 2));
    }
  }

  return nights;
}
```