To help navigation, sections are put into collapsible blocks. Click to toggle them.
Hover to see the value
Hover to see the value
You can hover a variable to see its value. This is supported by both Typst.app in browsers and VS Code with Tinymist. In Helix with Tinymist, you can use Space+K to show the value of the variable under the cursor. The same keymap applies to de facto LSP configuration in Neovim.
Copyable code
#let f(x) = {
x
}
#f[1]
#for _ in range(5) { f("X") }
#f[= Heading]
If the variable is a function, you have to make sure the function is actually called somewhere. Otherwise, typst can’t capture its value.
Put variables in the document
Put variables in the document
You can put variables directly in the document.
However, you might meet Error: Cannot join content with … occasionally:
Code
#set text(font: ("Libertinus Serif", "Noto Serif CJK SC"))
#show heading: it => {
it
text.font
}
= Heading
This is because your function returns multiples things, but they’re not compatible to be joined.
A minimal example of that error
#(
[= This is a content],
("This", "is", "an", "array"),
).join() // Error: Cannot join content with array
Docs: Defining functions
If the function body is a code block, the return value is the result of joining the values of each expression in the block.
Solution A: Convert it to the content type by wrapping with […] and switching to the markup mode.
#show heading: it => {
it
[#text.font] // 👈
}
- Pros: It’s highlighted.
- Cons:
- If the return value will be processed by other functions, then the
contenttype might be problematic. noneis omitted.
- If the return value will be processed by other functions, then the
Solution B: Convert it to the str type with the repr function.
#show heading: it => {
it
repr(text.font) // 👈
}
- Pros: The
strtype is simpler and sometimes safer to other functions. - Cons:
- No syntax highlighting.
- The result of
repris too verbose forheadingand other contents.
Inspect simple data with type()
Refer to Type Type – Typst Documentation for details.
Inspect document elements with it.fields()
Inspect document elements with it.fields()
it.fields() and it.func() is available.
- A
- B
- C
- D
Calling them in show list: it => { … } for the above example results in the following
Full code
#show list: it => {
set raw(lang:"typc")
[`it.func() =` #it.func()]
linebreak()
[`it.fields() =` #it.fields()]
}
- A
- B
- C
- D
With that said, auto completion / tab menu would suffice most needs.
Inspect any type or data structure with repr + assert
Inspect any type or data structure with repr + assert
Since .fields() is only available for content types, repr can do the same thing, but for any type. Its formatted output can be put in the document, or it can be paired with panic and assert. The main difference is that panic isn’t suited for multi-line strings (which repr returns), but assert.message can handle it just fine, though you’d need to pass false as condition to always show the error. This way the document can stay clean, and the important output can stay separate, to be able to see it at a glance.
Note that because of the contextual nature, panic/assert for some context code might not be triggered.
= Heading
#context {
let heading = query(heading).first(default: none)
// repr(heading)
// panic(repr(heading))
assert(false, message: repr(heading))
}
Example output with repr + assert
error: assertion failed: heading(
level: 1,
depth: 1,
offset: 0,
numbering: none,
supplement: [Section],
outlined: true,
bookmarked: auto,
hanging-indent: auto,
body: [Heading],
)
Pack complex content with data loading functions
Pack complex content with data loading functions
You can use data loading functions to encode anything.
They are handier than repr at times
#yaml.encode($ 1/2 x^2 $)
func: equation
block: true
body:
func: sequence
children:
- func: frac
num:
func: text
text: '1'
denom:
func: text
text: '2'
- func: space
- func: attach
base:
func: symbol
text: x
t:
func: text
text: '2'
Pass things to stderr as errors or warnings
Pass things to stderr with errors or warnings
If you are using the Typst CLI or debugging long documents, you might be interested in this.
Solution A: Use panic function.
#show list: it => {
panic(it) // 👈
}
- A
error: panicked with: list(
tight: true,
marker: ([•], [‣], [–]),
indent: 0pt,
body-indent: 0.5em,
spacing: auto,
children: (item(body: [A]),),
)
┌─ <stdin>:2:2
│
2 │ panic(it) // 👈
│ ^^^^^^^^^
help: error occurred while applying show rule to this list
┌─ <stdin>:4:0
│
4 │ - A
│ ^^^
- Pros:
- Show the value with a full backtrace.
- The value can be of any type.
- Cons: Only the first call can be captured.
Usually this can be circumvented withif condition { panic(it) }, but there exist cases that theconditionis too compilcated to write or just impossible to express within typst.
Docs: Note on function purity
In Typst, all functions are pure. This means that for the same arguments, they always return the same result. They cannot “remember” things to produce another value when they are called a second time.
Solution B: Exploit the font not found warning.
#show list: it => {
text(font: "[WARN]\n" + repr(it), "") // 👈
}
- A
warning: unknown font family: [warn]
list(
tight: true,
marker: ([•], [‣], [–]),
indent: 0pt,
body-indent: 0.5em,
spacing: auto,
children: (item(body: [a]),),
)
┌─ <stdin>:2:13
│
2 │ text(font: "[warn]" + repr(it), "") // 👈
│ ^^^^^^^^^^^^^^^^^^^
-
Pros: Can be used any number of times at any place.
-
Cons:
-
The backtrace only contains the call itself. No nested calls.
This implies that it’s not worth encapsulating it as a function (unless you combine it with aeval(generated-code)hack). -
The font name can only be strings. You may have to pack the value with
reproryaml.encode. (See the above sections.) -
The font name is case-insensitve.
You can circumvent this with the Label is not attached to anything warning, but that one will escape your strings, which means that you can’t wrap lines.warning: label `label("[WARN] list(\n tight: true,\n marker: ([•], [‣], [–]),\n indent: 0pt,\n body-indent: 0.5em,\n spacing: auto,\n children: (item(body: [A]),),\n)")` is not attached to anything
-
Limit divergent layout with layout-ltd
Limit divergent layout with layout-ltd
Use the layout-ltd package to debug Warning: Layout did not converge within 5 attempts.
#import "@preview/layout-ltd:0.1.0": layout-limiter
#show: layout-limiter.with(max-iterations: 2)
Finally, you can edit this post!
This is a wiki post that any user can edit.
By editing, you agree to dual-licensing under CC BY 4.0 and MIT.
This will conform with the forum guideline and reserve the possibility of submitting to the Typst Examples Book.
You can leave your name below after editing.





