Skip to content

Instantly share code, notes, and snippets.

@pufface
Last active June 1, 2020 00:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save pufface/6a2050b21a692d2400c35bbe3536552c to your computer and use it in GitHub Desktop.
Save pufface/6a2050b21a692d2400c35bbe3536552c to your computer and use it in GitHub Desktop.
class Square {
type = "Square" as const
constructor(public side: number) {}
}
class Circle {
type = "Circle" as const
constructor(public radius: number) {}
}
class Rectangle {
type = "Rectangle" as const
constructor(public width: number, public height: number) {}
}
type Shape = Square | Circle | Rectangle
type ShapeType = Shape["type"]
type ShapeMap<U> = { [K in ShapeType]: U extends { type: K } ? U : never }
type ShapeTypeMap = ShapeMap<Shape>
type Pattern<T> = { [K in keyof ShapeTypeMap]: (shape: ShapeTypeMap[K]) => T }
function matcher<T>(pattern: Pattern<T>): (shape: Shape) => T {
// https://github.com/Microsoft/TypeScript/issues/14107
return shape => pattern[shape.type](shape as any)
}
const shapes = [new Circle(4.0), new Square(5.0), new Rectangle(6.0, 7.0)]
const area = matcher<number>({
Square: square => square.side * square.side,
Circle: circle => circle.radius * circle.radius * Math.PI,
Rectangle: rect => rect.height * rect.width
})
const totalArea = shapes.reduce((acc, shape) => acc + area(shape), 0)
console.log(`Total area: ${totalArea}`)
const perimeter = matcher<number>({
Square: square => 4 * square.side,
Circle: circle => 2 * Math.PI * circle.radius,
Rectangle: rect => 2 * rect.height + 2 * rect.width
})
const sumPerimeter = shapes.reduce((acc, shape) => acc + perimeter(shape), 0)
console.log(`Total perimeter: ${sumPerimeter}`)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment