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
ismethod - Type Assertion - Convert between variants with the
asmethod - 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): RCreate 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: undefinedOff: var undefinedundefined,
// On state with optional brightness percentage
type On: (percentage?: number) => {
percentage: number;
}
On: (percentage: numberpercentage = 100) => ({ percentage: numberpercentage }),
});
// 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: numberpercentage } = 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: numberx: number) => ({ result: numberresult: x: numberx }),
},
"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: numberx: number, y: numbery: number) => ({ x: numberx, y: numbery }),
square: (x: number) => numbersquare: (x: numberx: number) => x: numberx,
},
"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.