DEV Community

Discussion on: Functional design: Algebraic Data Types

mdi profile image
mdl • Edited on

Great article !
What do you think about this approach to pattern matching ?
I think it looks a bit more declarative then checking the type manually.

type Tagged = {_tag: string}

type Tags<TUnionType extends Tagged> = TUnionType['_tag']

type TagTypeMap<TTags extends string, TUnionType extends Tagged> = {
  [TTag in TTags]: TUnionType extends {_tag: TTag} ? TUnionType : never

type Pattern<TResult, TTagTypeMap> = {
  [K in keyof TTagTypeMap]: (item: TTagTypeMap[K]) => TResult

type Patt<TResult, TUnionType extends Tagged> =
  Pattern<TResult, TagTypeMap<Tags<TUnionType>, TUnionType>> 

const match = <TUnionType extends Tagged, TObject extends TUnionType, TResult>
  (pattern: Patt<TResult, TUnionType>) =>
    (object: TObject) =>
      pattern[object._tag][object] as TResult

// ------------------Sample------------------
type Square = {len: number, _tag: 'Square'}
type Circle = {rad: number, _tag: 'Circle'}
type Rectangle = {xlen: number, ylen: number, _tag: 'Rectangle'}

type Shape = Square | Circle | Rectangle

const shapes = [
  {len: 1} as Square,
  {rad: 2} as Circle,
  {xlen: 3, ylen: 4} as Rectangle

const shapeDescription: Patt<string, Shape> = {
  Square: ({len}) => `I am square with length ${len}`,
  Circle: ({rad}) => `I am circle with radius ${rad}`,
  Rectangle: ({xlen, ylen}) => `I am rectangle with sides ${xlen} and ${ylen}`

const descriptions =
gcanti profile image
Giulio Canti Author

Check this out