```ts
export function gradeContainment(
  source: string[],
  draft: string[],
  span: number,
): number {
  if (!Array.isArray(source) || !Array.isArray(draft)) {
    throw new Error("Both source and draft must be arrays of strings.");
  }
  if (span <= 0 || !Number.isInteger(span)) {
    throw new Error("Span must be a positive whole number.");
  }
  if (source.some(word => typeof word !== "string" || word.trim() === "")) {
    throw new Error("All elements in source must be non-empty strings.");
  }
  if (draft.some(word => typeof word !== "string" || word.trim() === "")) {
    throw new Error("All elements in draft must be non-empty strings.");
  }
  if (draft.length < span) {
    throw new Error("Draft must contain at least as many words as the span.");
  }

  const stretchCount: Record<string, number> = {};
  for (let i = 0; i <= source.length - span; i++) {
    const stretch = source.slice(i, i + span).join(" ");
    if (stretchCount[stretch]) {
      stretchCount[stretch]++;
    } else {
      stretchCount[stretch] = 1;
    }
  }

  let matched = 0;
  for (let i = 0; i <= draft.length - span; i++) {
    const stretch = draft.slice(i, i + span).join(" ");
    if (stretchCount[stretch]) {
      matched++;
      stretchCount[stretch]--;
    }
  }

  return Math.floor((matched * 1000) / (draft.length - span + 1));
}
```