← Decorator lesson
Lab

Decorator

interface TextBlock {
  render(): string[];
}

// The source: the diagnostic lines themselves.
function plainText(lines: string[]): TextBlock {
  return { render: () => [...lines] };
}

// Numbers: the same interface, plus a prefix on every line it gets back.
function withNumbers(inner: TextBlock): TextBlock {
  return {
    render() {
      const lines = inner.render();
      return lines.map((line, i) => `${i + 1}: ${line}`);
    },
  };
}

// Frame: the same interface, plus a border around the lines it gets back.
function withFrame(inner: TextBlock): TextBlock {
  return {
    render() {
      const lines = inner.render();
      const width = Math.max(...lines.map((line) => line.length));
      const edge = `+${'-'.repeat(width + 2)}+`;
      return [edge, ...lines.map((line) => `| ${line.padEnd(width)} |`), edge];
    },
  };
}

// The command renders any TextBlock and never asks which layers it has.
function display(name: string, block: TextBlock) {
  const shown = block.render();
  console.log(`${name}:\n${shown.join('\n')}`);
}

const source = plainText(['check: links', 'broken: 2']);
const terminal = withNumbers(source);
const report = withFrame(withNumbers(source));
display('terminal', terminal);
display('report', report);