0.0.1 • Published 3 years ago

that-enum v0.0.1

Weekly downloads
-
License
MIT
Repository
github
Last release
3 years ago

Install

yarn add that-enum
// or
npm i that-enum

Usage

Defining an Enum

Ip Address

const IpAddr = Enum({
  V4: (a: number, b: number, c: number, d: number) => [a, b, c, d] as const,
  V6: (addr: string) => addr,
})

type IpAddr = typeof IpAddr.$type$

const home = IpAddr.V4(127, 0, 0, 1)

const loopback = IpAddr.V6('::1')

Animal

const Animal = Enum({
  Fox: null,
  Rabbit: null,
  Custom: (species: string) => species,
})

const nick = Animal.Fox()
const judy = Animal.Rabbit()
const flash = Animal.Custom('sloth')

match

Coin

const Coin = Enum({
  Penny: null,
  Nickel: null,
  Dime: null,
  Quarter: null,
})
type Coin = typeof Coin.$type$

function value_in_cents(coin: Coin): number {
  return match(coin)({
    Penny: () => {
      console.log('Lucky penny!')
      return 1
    },
    Nickel: () => 5,
    Dime: () => 10,
    Quarter: () => 25,
  })
}

Exhaustive matching

const Coin = Enum({
  Penny: null,
  Nickel: null,
  Dime: null,
  Quarter: null,
})
type Coin = typeof Coin.$type$

// Missing match arm for `Quarter`. Compiling error occurs.
function value_in_cents(coin: Coin): number {
  return match(coin)({
    Penny: () => {
      console.log('Lucky penny!')
      return 1
    },
    Nickel: () => 5,
    Dime: () => 10,
    // Quarter: () => 25,
  })
}

Using _ to catch cases aren't specified

const Coin = Enum({
  Penny: null,
  Nickel: null,
  Dime: null,
  Quarter: null,
})
type Coin = typeof Coin.$type$

// With `_`, the missing match arms won't causes compiling errors.
function value_in_cents(coin: Coin): number {
  return match(coin)({
    Penny: () => {
      console.log('Lucky penny!')
      return 1
    },
    // Nickel: () => 5,
    // Dime: () => 10,
    // Quarter: () => 25,
    _: () => 0,
  })
}

isVariantOf

import { isVariantOf, Enum } from 'that-enum'

const IpAddr = Enum({
  V4: (a: number, b: number, c: number, d: number) => [a, b, c, d] as const,
  V6: (addr: string) => addr,
})
type IpAddr = typeof IpAddr.$type$

const addr: IpAddr = getCurrentAddr()

if (isVariantOf(IpAddr.V4)(addr)) {
  addr // => infer to { type: 'V4', payload: [number, number, number, number] }
} else {
  addr // => infer to { type: 'V6', payload: string }
}

const home = IpAddr.V4(127, 0, 0, 1)

const loopback = IpAddr.V6('::1')

Option<T>

import { Option, Some, None } from 'that-enum'

it('match', () => {
  // Some
  const right = 1
  let optionNum = Some(right)
  let fn = jest.fn()
  match(optionNum)({
    Some(left) {
      fn(left)
    },
    None() {
      fn()
    },
  })
  expect(fn).toBeCalledWith(right)
  expect(fn).toBeCalledTimes(1)

  // None
  optionNum = None()
  fn = jest.fn()
  match(optionNum)({
    Some(left) {
      fn(left)
    },
    None() {
      fn()
    },
  })
  expect(fn).toBeCalledWith()
  expect(fn).toBeCalledTimes(1)
})

Limitation

that-enum is implemented in userland, not a built-in language feature. So, there are some limitations.

Recursive Type

// List implicitly has type 'any' because it does not have a type annotation and is referenced directly or indirectly in its own initializer ts(7022)
const List = Enum({
  Nil: null,
  Cons: (contains: number, tail: List) => ({ contains, tail }),
})
type List = typeof List.$type$

Generic Type

const Option = Enum({
  None: null,
  // `T` will be infered as `unkown`
  Some: <T>(value: T) => ({ value }),
})
type Option = typeof Option.$type$

Workaround

Option<T> has been supported by that-enum.