Last active
September 8, 2020 06:35
-
-
Save disco0/7595c99643c93998204eaa0002e266df to your computer and use it in GitHub Desktop.
Typescript Pattern Matching
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* @fileoverview | |
* Typescript Pattern Matching | |
* @description | |
* From [@fillopeter's Medium post](https://medium.com/@fillopeter/pattern-matching-with-typescript-done-right-94049ddd671c), | |
* _“Pattern matching” with Typescript done right_ | |
*/ | |
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 { | |
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}`) | |
document.getElementById("result").innerHTML = `Total area: ${totalArea} <br/> Total perimeter: ${sumPerimeter}` |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment