Skip to content

Matchbox Factories

The matchboxFactory function creates factories for working with tagged unions in TypeScript, providing type-safe pattern matching, exhaustiveness checking, and comprehensive type inference.

Its main features include:

  1. Factory Functions - Create tagged union instances with proper typing
  2. Pattern Matching - Match against union variants with exhaustiveness checking
  3. Type Predicates - Built-in type guards with the is method
  4. Type Assertion - Convert between variants with the as method
  5. Complete Type Inference - Full inference with minimal type annotations

You can create a factory using the matchboxFactory function. This factory then provides functions to create individual tagged union instances:

import {
function matchboxFactory<Config extends TaggedTypes | string, TagProp extends string = "tag", R = MatchboxFactory<Config extends readonly string[] ? { [K in Config[number]]: (data: any) => any; } : Config, TagProp>>(config: Config, tagProp?: TagProp): R
matchboxFactory
} from "matchina";
export const
const Light: MatchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
Light
=
matchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag", MatchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">>(config: {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, tagProp?: "tag" | undefined): MatchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
matchboxFactory
({
// Off state with no parameters
type Off: undefined
Off
:
var undefined
undefined
,
// On state with optional brightness percentage
type On: (percentage?: number) => {
percentage: number;
}
On
: (
percentage: number
percentage
= 100) => ({
percentage: number
percentage
}),
});
// Create light instances
const
const off: MatchboxMember<"Off", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
off
=
const Light: MatchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
Light
.
type Off: () => MatchboxMember<"Off", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
Off
();
const
const onFull: MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
onFull
=
const Light: MatchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
Light
.
type On: (percentage?: number | undefined) => MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
On
(); // Uses default 100%
const
const dimmed: MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
dimmed
=
const Light: MatchboxFactory<{
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
Light
.
type On: (percentage?: number | undefined) => MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
On
(50); // 50% brightness
const {
const percentage: number
percentage
} =
const dimmed: MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
dimmed
.
data: {
percentage: number;
}
data
;
export {
const off: MatchboxMember<"Off", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
export off
off
,
const onFull: MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
export onFull
onFull
,
const dimmed: MatchboxMember<"On", {
Off: undefined;
On: (percentage?: number) => {
percentage: number;
};
}, "tag">
export dimmed
dimmed
,
const percentage: number
export percentage
percentage
};

By default, the tag property is named tag, but you can customize it:

const
const states: MatchboxFactory<{
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, "key">
states
=
matchboxFactory<{
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, "key", MatchboxFactory<{
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, "key">>(config: {
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, tagProp?: "key" | undefined): MatchboxFactory<{
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, "key">
matchboxFactory
(
{
type Idle: () => {}
Idle
: () => ({}),
type Done: (x: number) => {
result: number;
}
Done
: (
x: number
x
: number) => ({
result: number
result
:
x: number
x
}),
},
"key"
);
const
const events: MatchboxFactory<{
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, "type">
events
=
matchboxFactory<{
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, "type", MatchboxFactory<{
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, "type">>(config: {
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, tagProp?: "type" | undefined): MatchboxFactory<{
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, "type">
matchboxFactory
(
{
add: (x: number, y: number) => {
x: number;
y: number;
}
add
: (
x: number
x
: number,
y: number
y
: number) => ({
x: number
x
,
y: number
y
}),
square: (x: number) => number
square
: (
x: number
x
: number) =>
x: number
x
,
},
"type"
);
const states: MatchboxFactory<{
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, "key">
states
.
type Idle: () => MatchboxMember<"Idle", {
Idle: () => {};
Done: (x: number) => {
result: number;
};
}, "key">
Idle
().
key: "Idle"
key
; // "Idle"
const events: MatchboxFactory<{
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, "type">
events
.
add: (x: number, y: number) => MatchboxMember<"add", {
add: (x: number, y: number) => {
x: number;
y: number;
};
square: (x: number) => number;
}, "type">
add
(1, 2).
type: "add"
type
; // "add"

Matchbox provides a powerful way to work with tagged unions in TypeScript, with additional type safety mechanisms, pattern matching, and improved developer experience.

This makes complex state modeling and data handling more robust and maintainable, with TypeScript providing excellent completion support and helping to catch errors at compile time rather than runtime.