Skip to main content
Version: 0.1.0

Matcher API

Schema Validation & Pattern Matching

The matcher module provides a robust structural validation system. It allows you to check if data matches a specific shape, type, or set of values using a "By Example" syntax or descriptive helpers.

:::info Mental Model The Matcher distinguishes between Fixed Shapes (defined by syntax like arrays and dictionaries) and Open Collections (defined by helpers like many or dict). :::

The Match Engine​

The core of the module is the match function, which validates a value against a pattern.

match​

Checks if a value satisfies a schema pattern.

matcher.match(value, expected, strict: false)
ParameterTypeDefaultDescription
valueanyRequiredThe data to validate.
expectedanyRequiredThe schema to match against (literal, type, structure, or descriptor).
strictboolfalseIf true, dictionaries are not allowed to have extra keys.

Pattern Syntax ("By Example")​

For fixed structures where you know the exact keys or length, use standard Typst syntax.

Types & Literals (Hybrid Matching)​

A type in the schema (e.g., int) performs a Hybrid Match. It matches:

  1. Instances of that type (e.g., 10).
  2. The Type Object itself (e.g., int).

This flexibility supports both standard data validation and meta-programming/configuration scenarios.

// 1. Instance Matching (Standard)
matcher.match(10, int) // true
matcher.match("foo", str) // true

// 2. Equality Matching (Meta-programming)
matcher.match(int, int) // true
matcher.match(int, float) // false

// 3. Literals
matcher.match("foo", "foo") // true
matcher.match(auto, auto) // true

:::note Controlling Specificity If you need to be more specific (e.g., "Must be a number, not the type int" or "Must be the type object int exactly"), use the Strict Descriptors instance and exact documented below. :::

Tuples (Fixed Arrays)​

A Typst array in the schema represents a Tuple: a list of fixed size where each position has a specific schema.

// Matches a pair: (Integer, String)
let schema = (int, str)

matcher.match((1, "a"), schema) // true
matcher.match((1, 1), schema) // false (wrong type at index 1)
matcher.match((1,), schema) // false (wrong length)

Records (Fixed Dictionaries)​

A Typst dictionary in the schema represents a Record: an object that must contain specific keys.

// Matches an object with specific fields
let user = (name: str, id: int)

matcher.match((name: "Alice", id: 1), user) // true
matcher.match((name: "Alice"), user) // false (missing key)

:::tip Partial Matches By default, match allows extra keys in dictionaries (partial matching). To forbid unknown keys, pass strict: true to the function or the switch case. :::


Descriptors ("By Description")​

For logical operations, open-ended collections, or strict type constraints, use these helper functions.

Strict Descriptors (Types)​

Use these when the default Hybrid Matching behavior is too broad.

instance​

Strictly enforces that the value is an instance of the type. Rejects the type object itself.

matcher.instance(type)

Example:

let pattern = matcher.instance(int)

matcher.match(10, pattern) // true
matcher.match(int, pattern) // false (Rejected!)

exact​

Strictly enforces equality. Use this to match specific values or type objects exactly, bypassing instance checks.

matcher.exact(value)

Example:

let pattern = matcher.exact(int)

matcher.match(int, pattern) // true
matcher.match(10, pattern) // false (Rejected!)

Logical & Collection Descriptors​

any (Wildcard)​

Matches anything. Use this when a field allows any value.

matcher.any()

choice​

Matches if any of the provided options match (Logic OR).

matcher.choice(..options)

Example:

// Matches an Integer OR a String
let id-schema = matcher.choice(int, str)

many​

Matches an array of any length where every item matches the schema (Homogeneous List).

matcher.many(schema)

Example:

// Matches a list of numbers: (1, 2, 3)
let numbers = matcher.many(int)

dict​

Matches a dictionary of any size where every value matches the schema (Homogeneous Map).

matcher.dict(schema)

Example:

// Matches a map of settings: (dark: true, silent: false)
#let settings = matcher.dict(bool)

Classification (Switch)​

The switch function allows you to categorize data against a list of cases, returning a value associated with the first match.

switch​

Evaluates a value against a list of cases.

matcher.switch(target, cases)
ParameterTypeDefaultDescription
targetanyRequiredThe value to classify.
casesarrayRequiredAn array of case objects.

case​

Defines a single branch in a switch statement.

matcher.case(pattern, output, strict: false)
ParameterTypeDefaultDescription
patternanyRequiredThe schema pattern to match.
outputanyRequiredThe value to return if this case matches.
strictboolfalseOverride strict mode for this specific case.

Example:

#let kind = matcher.switch(signal, {
import matcher: *

// 1. Match a specific shape
case((cost: int), "task")

// 2. Match a list of items
case(many(str), "tag-list")

// 3. Fallback using wildcard
case(any(), "unknown")
})