Custom functions
Typeflow ships a JSONata-level standard library with typed signatures (see Functions), plus use declarations to bring your own TypeScript functions into a mapping. Unknown functions are TF2007, wrong argument types are TF2008 — both at compile time.
When the standard library isn't enough, define your own functions. All three flavors are checked at compile time exactly like builtins — unknown names, wrong arity, and wrong argument types are compile errors.
In the language: fn
Define pure functions directly in the mapping — the body is a Typeflow expression over the parameters (the input is not in scope, so functions stay reusable and position-independent). The return type is optional: it is inferred from the body, and checked (TF2017) when declared. Functions can call builtins and functions declared above them — no forward references, so mappings stay terminating.
Fully serializable: fn definitions travel inside the compiled artifact, so they work everywhere the runtime does — including right here:
{ "name": "Ada Lovelace", "grades": [ { "value": 16, "grade": "A" }, { "value": 9, "grade": "C" }, { "value": 12, "grade": "B" } ] }
{
name: string;
grades: { value: number; grade: "A" | "B" | "C" }[];
}From TypeScript: use
Declare the typed signature in the .typeflow file; the implementation is imported from the module when the mapping is loaded:
use slugify(value: string): string from "./helpers"// helpers
export function slugify(value: string): string {
return value.toLowerCase().replace(/[^a-z0-9]+/g, '-');
}loadTypeflowMapping("./user.typeflow") and typeflow run import ./helpers automatically. With the low-level runtime API, pass implementations explicitly — instantiation fails fast if one is missing:
createMapping(compiled, { functions: { slugify } });From your app: defineFunction
Register functions once, application-side — no use line needed in the mappings. A definition is declared like a builtin: typed signature, doc, implementation:
import { defineFunction, compile, createMapping } from 'typeflowjs';
const slugify = defineFunction('slugify(value: string): string', {
doc: 'Lowercase, dash-separated slug.',
impl: (value) => String(value).toLowerCase().replace(/[^a-z0-9]+/g, '-'),
});
const result = compile(source, { functions: [slugify] });
const run = createMapping(result.compiled!, { functions: [slugify] });Optional parameters work (clamp(n: number, max?: number): number), the compiled artifact records which registered functions the mapping actually calls, and createMapping fails fast if one is missing. loadTypeflowMapping(path, { functions }) accepts them too.
Try it live
The playground can't import files, so it provides two demo implementations: slugify and capitalize (note the ? — optional parameters are supported):
{ "handle": "ada-lovelace", "display": "Ada" }
{ handle: string; display: string }Signatures are enforced like builtins — try slugify(42) above (TF2008), or rename a use to upper (TF2016: conflicts with a builtin).
This example is intentionally broken
Unknown functions are compile errors, and the diagnostic lists what's available:
// Fix the mapping errors to see the output.{ name: any }- 4:9errorTF2007Unknown function 'capitalizeWords'.hint: Available functions: string, length, contains, matches, upper, lower, trim, pad, substring, split, replace, base64encode, base64decode, encodeUrl, decodeUrl, encodeUrlComponent, decodeUrlComponent, number, formatBase, floor, ceil, round, abs, power, sqrt, random, sum, max, min, average, boolean, not, exists, count, join, append, zip, sort, reverse, shuffle, distinct, keys, values, lookup, merge, spread, type, now, millis, fromMillis, toMillis.