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.
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)
| Parameter | Type | Default | Description |
|---|---|---|---|
value | any | Required | The data to validate. |
expected | any | Required | The schema to match against (literal, type, structure, or descriptor). |
strict | bool | false | If 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:
- Instances of that type (e.g.,
10). - 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
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)
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)
| Parameter | Type | Default | Description |
|---|---|---|---|
target | any | Required | The value to classify. |
cases | array | Required | An array of case objects. |
case​
Defines a single branch in a switch statement.
matcher.case(pattern, output, strict: false)
| Parameter | Type | Default | Description |
|---|---|---|---|
pattern | any | Required | The schema pattern to match. |
output | any | Required | The value to return if this case matches. |
strict | bool | false | Override 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")
})