DEV Community

Cover image for Aljabr (الجبر): 0-deps TypeScript lib that fuses tagged unions / exhaustive matching + schema validation + reactive state + reactive UI
Ja
Ja

Posted on

Aljabr (الجبر): 0-deps TypeScript lib that fuses tagged unions / exhaustive matching + schema validation + reactive state + reactive UI

I’ve been working on this for a little over a month, wrote a couple of articles and tried to share it around without much luck so I figured I’d try again. I just released v0.3.10, which I’m treating as the first stable release. No further breaking changes are planned for the API surface so now is a good time to take a look.

Aljabr is a zero-dependency TypeScript library that treats tagged unions (algebraic data types) as the central primitive. You define a union once and get exhaustive pattern matching, schema validation, and reactive state management all from that single definition. The problem it solves is the fragmentation that happens when domain models, validation, and reactivity are handled by separate libraries. By unifying everything around unions, it makes application state behave like a compiler-checked finite state machine and eliminates whole categories of impossible-state bugs.

This an example from the Readme that demonstrates perfectly what I mean:

    /** @jsxImportSource aljabr/ui/dom */
    import { union, match, type Union } from "aljabr";
    import { Store, Derived } from "aljabr/prelude";
    import { createRenderer } from "aljabr/ui";
    import { domHost } from "aljabr/ui/dom";

    const Shape = union({
        Circle: (id: number, radius: number) => ({ id, radius }),
        Rect:   (id: number, w: number, h: number) => ({ id, w, h }),
    });
    type Shape = Union<typeof Shape>;

    const area = (s: Shape) => match(s, {
        Circle: ({ radius }) => Math.PI * radius ** 2,
        Rect:   ({ w, h })   => w * h,
    });

    const shapes = Store.create<Shape[]>([Shape.Circle(1, 5), Shape.Rect(2, 3, 4)]);
    const total  = Derived.create(() => shapes.reduce((sum, s) => sum + area(s), 0));

    const rows = shapes.map(
        s => <li>{area(s).toFixed(2)}</li>,
        { key: s => s.id },
    );

    const { mount } = createRenderer(domHost);
    mount(() =>
        <div>
            <ul>{rows}</ul>
            <p>Total: {() => total.get()?.toFixed(2)}</p>
            <button onClick={() => shapes.push(Shape.Circle(Date.now(), 10))}>
                Add Circle
            </button>
        </div>,
        document.body,
    );
Enter fullscreen mode Exit fullscreen mode

What’s in the box:

  • Tagged unions with match(), is.* wildcards, select() extraction
  • Result, Option, Validation, Signal, Derived, Store, List, Resource, Dispatcher, Scope
  • Schema decode/encode pipeline where errors surface as a Validation union
  • SolidJS-flavored reactive primitives and a pluggable UI layer (DOM + Canvas renderers)

Live CodeSandbox demo (no install needed): https://codesandbox.io/p/devbox/aljabr-demo-vtlr6t

Note: CodeSandbox's iframed browser screws with the canvas coordinates (I've already tracked this bug for future development) but, to use the demo fully, press the "Open in a new tab" button to normalize things.

If you prefer to run it locally: git clone the repo and npm run dev (or whatever package manager you use).

v0.3.10 (stable) release notes: https://github.com/jasuperior/aljabr/releases

Repo: https://github.com/jasuperior/aljabr

I’d love any kind of feedback, questions, bug reports, or even just a star if you like the idea.

Top comments (0)