Least Tiresome Exhaustive Matching in ST's MX
Overview: matchAs()()()
matchAs is a type-safe pattern matching utility for discriminated unions.
It never misses a case, it never drops to a fallback. It's completely
prepared for future code changes. It uses nearly zero JavaScript, but hides some
advanced TypeScript to know exactly what state your data is in.
Installation
npm install @arksouthern/stxImport
import matchAs from '@arksouthern/stx/mx'Basic Example
type AccountTab = { as: "accountTab", userInfo: DetailedUserInfo }type FriendsTab = { as: "friendsTab", userFriendList: Friends[] }type MessageTab = { as: "messageTab", selectedMessageThread: Msg[] }
type MxTab = AccountTab | FriendsTab | MessageTab
function selectTab(tab: MxTab) { return matchAs(tab)({ accountTab: x => userInfoHtml(x.userInfo), friendsTab: x => x.userFriendList[0], messageTab: x => checkLastMsg(x) })(tab)}You can safely write a function that take a union, different kinds of data,
then use matchAs to exactly handle every case.
If you add cases, or remove them, in the future, then instantly your matchAs
will alert you in TypeScript that an update is needed.
Key Features
No Fallback Cases, No Missing Cases Either
// ❌ TypeScript Error: Property 'messageTab' is missingmatchAs(tab)({ accountTab: x => userInfoHtml(x.userInfo), friendsTab: x => x.userFriendList[0] // Missing messageTab handler!})(tab)Always Exact Type, No Duck Checking, No Unknowns
matchAs(tab)({ accountTab: x => { // x is AccountTab x.userInfo // is available here }, friendsTab: x => { // x is FriendsTab x.userFriendList // is available here }, messageTab: x => { // x is MessageTab x.selectedMessageThread // is available here }})(tab)Return Type Inference
// Return type: HTMLElement | Friends | booleanconst result = matchAs(tab)({ accountTab: x => userInfoHtml(x.userInfo), // returns HTMLElement friendsTab: x => x.userFriendList[0], // returns Friends messageTab: x => checkLastMsg(x) // returns boolean})(tab)Performance-First In Action
// Create matcher onceconst matchTab = matchAs({} as MxTab)({ accountTab: x => userInfoHtml(x.userInfo), friendsTab: x => x.userFriendList[0], messageTab: x => checkLastMsg(x)});
// Reuse in looptabs.forEach(tab => { const result = matchTab(tab) // Process result...})Instructions
1. Type Requirements
Your discriminated union must:
- Use
asfor the discriminant property name - Have string literal types for the
asvalues - Define
ason all variants
// ✅ Correcttype Good = | { as: "option1", data: string } | { as: "option2", count: number }
// ❌ Won't work - uses 'type' instead of 'as'type Bad = | { type: "option1", data: string } | { type: "option2", count: number }2. Usage
matchAs works with discriminated unions that use an as property as their discriminant.
It enforces exhaustive matching: TypeScript will error if any variant is unhandled.
matchAs uses a curried API with three stages:
matchAs(union)- Pass a representative value from your union type({ ... handlers ... })- Provide handlers for each variant(value)- Pass the actual value to match
Exhaustive Pattern Matching with matchAs()
(1) Setting up the matcher
┌─────────────┐
│ │
│ matchAs() │
│ │
└──────┬──────┘
│
│ ┌──────────────────┐
│ │ union: U │
| | |
│ │ (discriminant) │
│ └────────┬─────────┘
│ │
└───────────────►│
│
▼
┌─────────────────┐
│ Matcher Factory │
└────────┬────────┘
│
(2) Providing exhaustive handlers
┌────────────────────────────┐
│ handlers: { │
│ │
│ variantA: (x) => ..., │
│ │
│ variantB: (x) => ..., │
│ │
│ variantC: (x) => ... │
│ │
│ } │
└──────────┬─────────────────┘
│
│ TypeScript enforces
|
│ all variants present
│
▼
┌─────────────────┐
│ Runtime Matcher │
└────────┬────────┘
│
(3) Matching on actual value
┌─────────────────┐
│ value: V │
| |
│ (actual data) │
└────────┬────────┘
│
│ value.as determines
|
│ which handler runs
│
▼
┌─────────────────────────────────┐
│ handlers[value.as](value) │
│ │
│ Returns: ReturnType<F[V["as"]]> │
└─────────────────────────────────┘
(4) Full flow example
matchAs(tab) ({ (currentTab)
accountTab: x => ...,
│ friendsTab: x => ..., │
│ messageTab: x => ... │
│ }) │
│ │ │
└─────────────────────►└────────────────────────────►│
│
┌───────────────────────────────────────┘
│
▼
┌─────────────────────────────┐
│ Type-safe, exhaustive match │
| |
│ with full inference │
└─────────────────────────────┘
Exhaustive Matching Using ST's MX
ArkSouthern
| Authored | Apr 3rd 2022 |
| Updated | Jan 25th 2026 |
| ID | n-rd-stg |