that-enum
v0.0.1
Published
<!-- # Example
Downloads
24
Readme
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
.