| 1 | import { describe, it } from "@effect/vitest" |
| 2 | import { Match, pipe } from "effect" |
| 3 | import { strictEqual } from "./utils/assert.ts" |
| 4 | |
| 5 | describe("Match", () => { |
| 6 | it("fn keeps the selector arguments", () => { |
| 7 | type Todo = { readonly completed: boolean } |
| 8 | type Todos = Array<Todo> |
| 9 | type Filter = "All" | "Active" | "Completed" |
| 10 | |
| 11 | const filterTodos = Match.fn((_todos: Todos, filter: Filter) => filter).pipe( |
| 12 | Match.withReturnType<Todos>(), |
| 13 | Match.when("All", (_filter, todos) => todos), |
| 14 | Match.when("Active", (_filter, todos) => todos.filter((todo) => !todo.completed)), |
| 15 | Match.when("Completed", (_filter, todos) => todos.filter((todo) => todo.completed)), |
| 16 | Match.exhaustive |
| 17 | ) |
| 18 | const todos: Todos = [{ completed: false }, { completed: true }] |
| 19 | |
| 20 | strictEqual(filterTodos(todos, "All"), todos) |
| 21 | strictEqual(filterTodos(todos, "Active").length, 1) |
| 22 | strictEqual(filterTodos(todos, "Active")[0], todos[0]) |
| 23 | strictEqual(filterTodos(todos, "Completed").length, 1) |
| 24 | strictEqual(filterTodos(todos, "Completed")[0], todos[1]) |
| 25 | }) |
| 26 | |
| 27 | it("fn can match a projected value", () => { |
| 28 | type Todo = { readonly status: "Active" | "Completed"; readonly title: string } |
| 29 | const formatTodo = Match.fn((todo: Todo) => todo.status).pipe( |
| 30 | Match.when("Active", (_status, todo) => `Active: ${todo.title}`), |
| 31 | Match.when("Completed", (_status, todo) => `Completed: ${todo.title}`), |
| 32 | Match.exhaustive |
| 33 | ) |
| 34 | |
| 35 | strictEqual(formatTodo({ status: "Active", title: "Write tests" }), "Active: Write tests") |
| 36 | }) |
| 37 | |
| 38 | it("tag matches an exact _tag and falls through for null", () => { |
| 39 | const match = pipe( |
| 40 | Match.type<{ _tag: "A" } | null>(), |
| 41 | Match.tag("A", () => "hit"), |
| 42 | Match.orElse(() => "miss") |
| 43 | ) |
| 44 | |
| 45 | strictEqual(match({ _tag: "A" }), "hit") |
| 46 | strictEqual(match(null), "miss") |
| 47 | }) |
| 48 | |
| 49 | it("tagStartsWith matches a _tag prefix and falls through otherwise", () => { |
| 50 | const match = pipe( |
| 51 | Match.type<{ _tag: "A.one" } | null>(), |
| 52 | Match.tagStartsWith("A", () => "hit"), |
| 53 | Match.orElse(() => "miss") |
| 54 | ) |
| 55 | |
| 56 | strictEqual(match({ _tag: "A.one" }), "hit") |
| 57 | strictEqual(match(null), "miss") |
| 58 | }) |
| 59 | |
| 60 | it("checks symbol-keyed object pattern properties", () => { |
| 61 | const key = Symbol("key") |
| 62 | const match = pipe( |
| 63 | Match.type<{ readonly [key]: "expected" | "other" }>(), |
| 64 | Match.when({ [key]: "expected" }, () => "hit"), |
| 65 | Match.orElse(() => "miss") |
| 66 | ) |
| 67 | |
| 68 | strictEqual(match({ [key]: "expected" }), "hit") |
| 69 | strictEqual(match({ [key]: "other" }), "miss") |
| 70 | strictEqual(match({} as any), "miss") |
| 71 | }) |
| 72 | }) |