Skip to main content
Version: Canary 🚧

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.

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
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)
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")
})