Last active
May 25, 2020 08:52
-
-
Save nrkn/7d1e202591216c8045ea0a31572c1914 to your computer and use it in GitHub Desktop.
TypeScript pattern matching with predicates
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
| import { Shape } from './shapes.types' | |
| import { area, perimeter } from './shapes.lib' | |
| const shapes: Shape[] = [ | |
| { radius: 4 }, { side: 5 }, { width: 6, height: 7 } | |
| ] | |
| const totalArea = shapes.reduce( | |
| ( sum, shape ) => sum + area( shape ), | |
| 0 | |
| ) | |
| const totalPerimeter = shapes.reduce( | |
| ( sum, shape ) => sum + perimeter( shape ), | |
| 0 | |
| ) | |
| console.log( { totalArea, totalPerimeter } ) |
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
| import { TypePredicates, TypeMapper } from './types' | |
| export const matcher = <KeyMapper,T>( | |
| predicates: TypePredicates<KeyMapper>, mapper: TypeMapper<KeyMapper,T> | |
| ) => | |
| <K extends keyof KeyMapper>( value: KeyMapper[ K ] ) => { | |
| const predicateKeys = Object.keys( predicates ) as K[] | |
| const key = predicateKeys.find( key => predicates[ key ]( value ) ) | |
| if ( key === undefined ) | |
| throw Error( | |
| `Expected value to match one of: ${ predicateKeys.join( ', ' ) }` | |
| ) | |
| return mapper[ key ]( value ) | |
| } |
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
| import { matcher } from './matcher' | |
| import { ShapeMapper } from './shapes.types' | |
| import { shapePredicates } from './shapes.predicates' | |
| const areaMapper: ShapeMapper<number> = { | |
| Square: square => square.side * square.side, | |
| Circle: circle => circle.radius * Math.PI, | |
| Rectangle: rectangle => rectangle.width * rectangle.height | |
| } | |
| const perimeterMapper: ShapeMapper<number> = { | |
| Square: square => square.side * 4, | |
| Circle: circle => 2 * Math.PI * circle.radius, | |
| Rectangle: rect => rect.width * 2 + rect.height * 2 | |
| } | |
| export const area = matcher( shapePredicates, areaMapper ) | |
| export const perimeter = matcher( shapePredicates, perimeterMapper ) |
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
| import { Circle, Square, Rectangle, ShapePredicates } from './shapes.types' | |
| export const isNumber = ( value: any ): value is number => | |
| typeof value === 'number' && !isNaN( value ) | |
| export const isCircle = ( value: any ): value is Circle => | |
| value && isNumber( value.radius ) | |
| export const isSquare = ( value: any ): value is Square => | |
| value && isNumber( value.side ) | |
| export const isRectangle = ( value: any ): value is Rectangle => | |
| value && isNumber( value.width ) && isNumber( value.height ) | |
| export const shapePredicates: ShapePredicates = { | |
| Circle: isCircle, | |
| Square: isSquare, | |
| Rectangle: isRectangle | |
| } |
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
| import { TypeMapper, TypePredicates } from './types' | |
| export interface Circle { | |
| radius: number | |
| } | |
| export interface Square { | |
| side: number | |
| } | |
| export interface Rectangle { | |
| width: number | |
| height: number | |
| } | |
| export type Shape = Circle | Square | Rectangle | |
| export interface ShapeKeyMapper { | |
| Circle: Circle | |
| Square: Square | |
| Rectangle: Rectangle | |
| } | |
| export type ShapeMapper<T> = TypeMapper<ShapeKeyMapper, T> | |
| export type ShapePredicates = TypePredicates<ShapeKeyMapper> |
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
| export type TypeMapper<KeyMapper, T> = { | |
| [ K in keyof KeyMapper ]: ( value: KeyMapper[ K ] ) => T | |
| } | |
| export type TypePredicates<KeyMapper> = { | |
| [ K in keyof KeyMapper ]: ( value: any ) => value is KeyMapper[ K ] | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment