Skip to content

Instantly share code, notes, and snippets.

@Herve07h22
Created June 23, 2023 12:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save Herve07h22/acda50482d5c1d40ee1bf4b204be575e to your computer and use it in GitHub Desktop.
Save Herve07h22/acda50482d5c1d40ee1bf4b204be575e to your computer and use it in GitHub Desktop.
Pattern matching instead of Visitor pattern
import { match, P } from "ts-pattern";
const headerBlocType = {
Header: "header",
Paragraph: "paragraph",
Image: "image",
} as const;
interface HeadBlockData {
text: string;
level: 1 | 2 | 3 | 4 | 5 | 6;
}
interface ParagraphBlockData {
text: string;
}
interface ImageBlockData {
url: string;
caption: string;
}
export class HeaderBlock {
type = headerBlocType.Header;
constructor(public data: HeadBlockData) {}
}
export class ParagraphBlock {
type = headerBlocType.Paragraph;
constructor(public data: ParagraphBlockData) {}
}
export class ImageBlock {
type = headerBlocType.Image;
constructor(public data: ImageBlockData) {}
}
// Type union instead of inheritance !
export type Block = HeaderBlock | ParagraphBlock | ImageBlock;
export class BlockEditor {
blocks: Array<Block> = [];
addBlock(block: Block) {
this.blocks.push(block);
}
removeBlock(block: Block) {
const index = this.blocks.indexOf(block);
index !== -1 && this.blocks.splice(index, 1);
}
outputHtml() {
return this.blocks.reduce((html, block) => html + renderToHtml(block), "");
}
}
// This is where the pattern matching shines
function renderToHtml(block: Block) {
return match(block)
.with(
{ type: "header", data: P.select() },
({ level, text }) => `<h${level}>${text}</h${level}>`
)
.with(
{ type: "paragraph", data: P.select() },
({ text }) => `<p>${text}</p>`
)
.with(
{ type: "image", data: P.select() },
({ url, caption }) => `<img src="${url}" alt="${caption}" />`
)
.exhaustive();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment