The mental model
Start with these five ideas, then use the linked pages for syntax and examples.
1. A .tsi file is TypeScript plus two constructs
Section titled “1. A .tsi file is TypeScript plus two constructs”A .tsi file uses TypeScript for types, functions and control flow. Nola adds syntax for inference, including infer function, ask and contextual parameters.
export infer function analyzeUserRequest(userId: string, .message: string) { const ticketId = ask `ticket id mentioned in the message`: string; const isFraud = ask `does the message look fraudulent`: boolean; return { userId, ticketId, isFraud };}2. Intents are lazy values
Section titled “2. Intents are lazy values”Calling an infer function returns an Intent<T> without running its body. An extractor such as ..`…`: T also creates a request without sending it to a model. You can build either value now and resolve it later. See Intents.
3. ask is to intents what await is to promises
Section titled “3. ask is to intents what await is to promises”ask resolves an askable: an extractor, a call intent, or the intent returned by another infer function. It has the same precedence as await. There are two rules to keep in mind:
askis legal only directly inside aninfer functionbody or directly in the module body — not inside a plain function or a nested closure (NOLA2001). A top-levelaskruns as its own<module>invocation.- From plain TypeScript you do not
ask; youawaitthe intent an infer function returns. A raw extractor or call intent is never bare-awaited — it has no context of its own and throwsNOLA3010.
4. One dot in, ask out
Section titled “4. One dot in, ask out”A parameter with a leading dot, such as .message, is a contextual parameter. Its value is included in the prompt for every ask in that invocation. A plain parameter such as userId stays available to your code; the prompt does not include it.
An extractor requests a typed answer: ask `instruction`: T. In a call intent the argument is written the same way, createTicket(`a short title`: string). When you store an extractor in a variable, add two dots: ..`instruction`: T.
5. Lowering: the JSX model
Section titled “5. Lowering: the JSX model”The Nola toolchain compiles .tsi into plain TypeScript, much as a JSX transform converts markup into function calls. nola build, nola run, nola check and the editor integration share this transformation, called lowering. Your project needs the Nola tools to read .tsi; downstream tools then work with the generated TypeScript.
Vocabulary
Section titled “Vocabulary”| Term | Meaning |
|---|---|
| infer function | infer function name(…) { … } — an LLM-backed function; calling it returns an Intent<T> |
| contextual parameter | .name: T — a parameter whose value is shown to the model |
| intent | the lazy value ask (or await, from plain TS) resolves — see Intents |
| askable | anything ask accepts: an extractor, a call intent, an infer function’s intent |
| extractor | ask `instruction`: T (..`instruction`: T when stored, away from ask and a call’s arguments) — a request to pull a T from context |
| call intent | fn`hint`(…) or a plain call with an extractor argument — the model fills the arguments, then the function runs |
| context statement | a bare template literal on its own line, optionally continued with values (`Page` oncall `when …`) — text every later ask in the scope sees, read at each ask |
| instruction | the backtick text you write: a context statement, an extractor’s prompt, a call hint |
| prompt | what a provider sends; the default rendering is described in The prompt, and receipts carry what was sent |
| invocation | one execution of an infer function — the scope every ask inside it shares |
| provider | the service supplying inference, such as OpenAI; a configured model instance goes in the model entry of nola.config.ts |
Next: Intents