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:
- Factory Functions - Create tagged union instances with proper typing
- Pattern Matching - Match against union variants with exhaustiveness checking
- Type Predicates - Built-in type guards with the
is
method - Type Assertion - Convert between variants with the
as
method - Complete Type Inference - Full inference with minimal type annotations
Creating a Factory
Section titled “Creating a Factory”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
Create a tagged union from a record mapping tags to value types, along with associated
variant constructors, type predicates and `match` function.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: {
...;
}, tagProp?: "tag" | undefined): MatchboxFactory<...>
Create a tagged union from a record mapping tags to value types, along with associated
variant constructors, type predicates and `match` function.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 };
Custom Tag Property
Section titled “Custom Tag Property”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<...>
Create a tagged union from a record mapping tags to value types, along with associated
variant constructors, type predicates and `match` function.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: {
...;
}, tagProp?: "type" | undefined): MatchboxFactory<...>
Create a tagged union from a record mapping tags to value types, along with associated
variant constructors, type predicates and `match` function.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"
Conclusion
Section titled “Conclusion”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.