This repository has been archived on 2026-03-28. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
StasisWarden-Legacy/node_modules/structural/test/match.ts
T

124 lines
4.0 KiB
TypeScript

import * as t from ".."
describe(t.CaseSwitch, () => {
test("cannot construct with zero cases", () => {
expect(() => {
new t.CaseSwitch([])
}).toThrow()
})
test("type acceptance is memoized", () => {
const matcher = t.when(t.str, () => 'str')
.when(t.num, () => 'num')
expect(matcher.accept).toBe(matcher.accept)
})
})
test("returns value", () => {
expect(
t.match(
5, t.when(t.str, x => `hello ${x}`)
.when(t.num, x => x * 2))
).toBe(10)
})
test("enforces exhaustive match", () => {
const handle = (x: number | string) => {
const matcher = t
.when(t.value(5 as 5), (x: 5) => x + 1)
.when(t.num, (x: number) => x * x)
// Try commenting out this arm: compilation will fail if you do
.when(t.str, x => x.length)
return t.match(x, matcher)
}
expect(handle('foo')).toBe(3)
expect(handle(5)).toBe(6)
expect(handle(2)).toBe(4)
})
test("throws a type error if none match", () => {
class Animal {}
class Dog extends Animal {}
class Cat extends Animal {}
const matcher = t.when(t.str, x => `hello ${x}`)
.when(t.instanceOf(Dog), _ => 'dog')
.when(t.instanceOf(Animal), _ => 'animal')
expect(t.match(new Dog(), matcher)).toBe('dog')
expect(t.match(new Cat(), matcher)).toBe('animal')
expect(t.match(new Animal(), matcher)).toBe('animal')
expect(() => {
t.match(5 as any, matcher)
}).toThrow();
})
test("exhaustiveness example with t.Kind", () => {
const fn = (u: t.Kind) => t.match(u,
t.when(t.instanceOf(t.Any), () => 'any')
.when(t.instanceOf(t.Never), () => 'never')
.when(t.instanceOf(t.SetType), () => 'set')
.when(t.instanceOf(t.MapType), () => 'map')
.when(t.instanceOf(t.Dict), () => 'dict')
.when(t.instanceOf(t.Struct), () => 'struct')
.when(t.instanceOf(t.Arr), () => 'array')
.when(t.instanceOf(t.Value), () => 'value')
.when(t.instanceOf(t.InstanceOf), () => 'instanceof')
.when(t.instanceOf(t.TypeOf), () => 'typeof')
.when(t.instanceOf(t.Either), () => 'either')
.when(t.instanceOf(t.DefaultIntersect), () => 'default-intersect')
.when(t.instanceOf(t.MergeIntersect), () => 'merge-intersect')
.when(t.instanceOf(t.Validation), () => 'validation')
.when(t.instanceOf(t.Is), () => 'is'))
expect(fn(t.any)).toBe('any')
expect(fn(t.value(1).or(t.value('two')))).toBe('either')
})
test("kind example", () => {
// This example demonstrates that
function visitTypes(type: t.Kind, pre: (x: t.Kind) => void, post: (x: t.Kind) => void) {
const recur = (x: t.Kind) => visitTypes(x, pre, post)
const ignore = (_: t.Kind) => {}
pre(type)
t.match(
type,
t.when(t.instanceOf(t.Any), ignore)
.when(t.instanceOf(t.Never), ignore)
.when(t.instanceOf(t.SetType), v => recur(v.valueType))
.when(t.instanceOf(t.MapType), v => { recur(v.keyType); recur(v.valueType) })
.when(t.instanceOf(t.Dict), v => recur(v.valueType))
.when(t.instanceOf(t.Struct), v =>
Object.keys(v.definition).forEach(k => recur(v.definition[k])))
.when(t.instanceOf(t.Arr), v => recur(v.elementType))
.when(t.instanceOf(t.Value), ignore)
.when(t.instanceOf(t.InstanceOf), ignore)
.when(t.instanceOf(t.TypeOf), ignore)
.when(t.instanceOf(t.Either), v => { recur(v.l); recur(v.r) })
.when(t.instanceOf(t.DefaultIntersect), v => { recur(v.l); recur(v.r) })
.when(t.instanceOf(t.MergeIntersect), v => { recur(v.l); recur(v.r) })
.when(t.instanceOf(t.Validation), ignore)
.when(t.instanceOf(t.Is), ignore)
)
post(type)
}
function visitType(type: t.Kind) {
const pre = (_: t.Kind) => {};
const post = (_: t.Kind) => {};
visitTypes(type, pre, post);
}
visitType(t.subtype({
foo: t.num,
bar: t.num,
baz: t.subtype({
wat: t.bool,
cow: t.value('wat')
}).or(t.subtype({
wat: t.str,
cow: t.value('str')
}))
}))
})