```ts
export function gradeContainment(
  source: string[],
  draft: string[],
  span: number,
): number {
  if (!Number.isInteger(span) || span <= 0) {
    throw new Error("span must be a positive whole number");
  }
  if (!Array.isArray(source) || !Array.isArray(draft)) {
    throw new Error("source and draft must be arrays");
  }
  if (source.some(word => typeof word !== 'string' || word.length === 0)) {
    throw new Error("all source elements must be non-empty strings");
  }
  if (draft.some(word => typeof word !== 'string' || word.length === 0)) {
    throw new Error("all draft elements must be non-empty strings");
  }
  if (draft.length < span) {
    return 0;
  }

  const seen = new Map<string, number>();
  for (let i = 0; i + span <= source.length; i++) {
    const stretch = source.slice(i, i + span).join(" ");
    seen.set(stretch, (seen.get(stretch) || 0) + 1);
  }

  let matched = 0;
  let total = 0;
  for (let i = 0; i + span <= draft.length; i++) {
    total += 1;
    const stretch = draft.slice(i, i + span).join(" ");
    if (seen.has(stretch) && seen.get(stretch)! > 0) {
      matched += 1;
      seen.set(stretch, seen.get(stretch)! - 1);
    }
  }

  return Math.floor((matched / total) * 1000);
}
```