```ts
export function fillDeskMap(plan: string[], legend: Record<string, string[]>): Record<string, unknown> {
  if (!Array.isArray(plan) || plan.length === 0) {
    throw new Error('Invalid floor plan');
  }
  const rowLength = plan[0].length;
  for (const row of plan) {
    if (typeof row !== 'string' || row.length === 0) {
      throw new Error('Invalid floor row');
    }
    if (row.length !== rowLength) {
      throw new Error('Floor rows must be the same length');
    }
    for (const char of row) {
      if (!['#', '.', /[a-z]/].includes(char)) {
        throw new Error('Invalid character in floor plan');
      }
    }
  }

  const legendKeys = Object.keys(legend);
  for (const key of legendKeys) {
    if (key.length !== 1 || !/[a-z]/.test(key)) {
      throw new Error('Legend keys must be a single small letter');
    }
    if (!plan.some(row => row.includes(key))) {
      throw new Error('Legend key names a bank not on the floor');
    }
    if (!Array.isArray(legend[key]) || legend[key].some(name => typeof name !== 'string' || name.length === 0)) {
      throw new Error('Legend values must be lists of non-empty strings');
    }
  }

  const deskMap: Record<string, unknown> = { floor: [], sat: [], spare: 0 };
  for (const row of plan) {
    deskMap.floor.push(row.split('').map(char => char === '.' ? '.' : '#'));
  }

  for (const bank in legend) {
    const people = legend[bank];
    let personIndex = 0;
    for (let r = 0; r < plan.length; r++) {
      for (let c = 0; c < rowLength; c++) {
        if (plan[r][c] === bank && personIndex < people.length) {
          deskMap.floor[r][c] = people[personIndex].charAt(0).toUpperCase();
          deskMap.sat.push(`${people[personIndex]} r${r} c${c}`);
          personIndex++;
        } else if (plan[r][c] === bank) {
          throw new Error('One name handed two desks');
        }
      }
    }
  }

  deskMap.spare = plan.reduce((acc, row) => acc + row.split('').filter(char => char === '#').length, 0);
  return deskMap;
}
```